diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..5c5fb8a --- /dev/null +++ b/404.html @@ -0,0 +1,1287 @@ + + + + + + + + + + + + + + + + + + + + + + + IntelOwl Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/GoIntelOwl/Banner.png b/GoIntelOwl/Banner.png new file mode 100644 index 0000000..ca406d3 Binary files /dev/null and b/GoIntelOwl/Banner.png differ diff --git a/GoIntelOwl/index.html b/GoIntelOwl/index.html new file mode 100644 index 0000000..5d2473d --- /dev/null +++ b/GoIntelOwl/index.html @@ -0,0 +1,752 @@ + + + + + + + + + + + + + +index - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Go-IntelOwl Repository

+

go-intelowl

+

GitHub issues +GitHub license

+

go-banner +go-intelowl is a client library/SDK that allows developers to easily automate and integrate IntelOwl with their own set of tools!

+ +

Table of Contents

+ +

Getting Started

+

Pre requisites

+
    +
  • Go 1.17+
  • +
+

Installation

+

Use go get to retrieve the SDK to add it to your GOPATH workspace, or project's Go module dependencies.

+
$ go get github.com/intelowlproject/go-intelowl
+
+

Usage

+

This library was built with ease of use in mind! Here are some quick examples to get you started. If you need more example you can go to the examples directory

+

To start using the go-intelowl library you first need to import it:

+
import "github.com/intelowlproject/go-intelowl/gointelowl"
+
+

Construct a new IntelOwlClient, then use the various services to easily access different parts of Intelowl's REST API. Here's an example of getting all jobs:

+
clientOptions := gointelowl.IntelOwlClientOptions{
+    Url:         "your-cool-URL-goes-here",
+    Token:       "your-super-secret-token-goes-here",
+    // This is optional
+    Certificate: "your-optional-certificate-goes-here",
+}
+
+intelowl := gointelowl.NewIntelOwlClient(
+    &clientOptions,
+    nil
+)
+
+ctx := context.Background()
+
+// returns *[]Jobs or an IntelOwlError!
+jobs, err := intelowl.JobService.List(ctx)
+
+

For easy configuration and set up we opted for options structs. Where we can customize the client API or service endpoint to our liking! For more information go here. Here's a quick example!

+
// ...Making the client and context!
+
+tagOptions = gointelowl.TagParams{
+  Label: "NEW TAG",
+  Color: "#ffb703",
+}
+
+createdTag, err := intelowl.TagService.Create(ctx, tagOptions)
+if err != nil {
+    fmt.Println(err)
+} else {
+    fmt.Println(createdTag)
+}
+
+

Examples

+

The examples directory contains a couple for clear examples, of which one is partially listed here as well:

+
package main
+
+import (
+    "fmt"
+
+    "github.com/intelowlproject/go-intelowl/gointelowl"
+)
+
+func main(){
+    intelowlOptions := gointelowl.IntelOwlClientOptions{
+        Url:         "your-cool-url-goes-here",
+        Token:       "your-super-secret-token-goes-here",
+        Certificate: "your-optional-certificate-goes-here",
+    }
+
+    client := gointelowl.NewIntelOwlClient(
+        &intelowlOptions,
+        nil,
+    )
+
+    ctx := context.Background()
+
+    // Get User details!
+    user, err := client.UserService.Access(ctx)
+    if err != nil {
+        fmt.Println("err")
+        fmt.Println(err)
+    } else {
+        fmt.Println("USER Details")
+        fmt.Println(*user)
+    }
+}
+
+

For complete usage of go-intelowl, see the full package docs.

+

Contribute

+

If you want to follow the updates, discuss, contribute, or just chat then please join our slack channel we'd love to hear your feedback!

+

License

+

Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE.

+

Links

+ +

FAQ

+

Generate API key

+

You need a valid API key to interact with the IntelOwl server.

+

v4.0 and above

+

You can get an API by doing the following:

+
    +
  1. Log / Signin into intelowl
  2. +
  3. At the upper right click on your profile from the drop down select API Access/ Sessions
  4. +
  5. Then generate an API key or see it!
  6. +
+

v4.0 below

+

Keys should be created from the admin interface of IntelOwl: you have to go in the Durin section (click on Auth tokens) and generate a key there.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/GreedyBear/Api-docs/index.html b/GreedyBear/Api-docs/index.html new file mode 100644 index 0000000..19a9d22 --- /dev/null +++ b/GreedyBear/Api-docs/index.html @@ -0,0 +1,620 @@ + + + + + + + + + + + + + +API docs - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/GreedyBear/Api-docs/swagger-92a9f028.html b/GreedyBear/Api-docs/swagger-92a9f028.html new file mode 100644 index 0000000..1993b10 --- /dev/null +++ b/GreedyBear/Api-docs/swagger-92a9f028.html @@ -0,0 +1,125 @@ + + + + + + Swagger UI + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/GreedyBear/Contribute/index.html b/GreedyBear/Contribute/index.html new file mode 100644 index 0000000..22f09da --- /dev/null +++ b/GreedyBear/Contribute/index.html @@ -0,0 +1,1065 @@ + + + + + + + + + + + + + +Contribute - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Contribute

+

General Guidance

+

Please refer to IntelOwl Documentation for everything missing here.

+

Rules

+

GreedyBear welcomes contributors from anywhere and from any kind of education or skill level. We strive to create a community of developers that is welcoming, friendly and right.

+

For this reason it is important to follow some easy rules based on a simple but important concept: Respect.

+
    +
  • +

    Before starting to work on an issue, you need to get the approval of one of the maintainers. Therefore please ask to be assigned to an issue. If you do not that but you still raise a PR for that issue, your PR can be rejected. This is a form of respect for both the maintainers and the other contributors who could have already started to work on the same problem.

    +
  • +
  • +

    When you ask to be assigned to an issue, it means that you are ready to work on it. When you get assigned, take the lock and then you disappear, you are not respecting the maintainers and the other contributors who could be able to work on that. So, after having been assigned, you have a week of time to deliver your first draft PR. After that time has passed without any notice, you will be unassigned.

    +
  • +
  • +

    Before asking questions regarding how the project works, please read through all the documentation and install the project on your own local machine to try it and understand how it basically works. This is a form of respect to the maintainers.

    +
  • +
  • +

    Once you started working on an issue and you have some work to share and discuss with us, please raise a draft PR early with incomplete changes. This way you can continue working on the same and we can track your progress and actively review and help. This is a form of respect to you and to the maintainers.

    +
  • +
  • +

    When creating a PR, please read through the sections that you will find in the PR template and compile it appropriately. If you do not, your PR can be rejected. This is a form of respect to the maintainers.

    +
  • +
+

Code Style

+

Keeping to a consistent code style throughout the project makes it easier to contribute and collaborate. We make use of psf/black and isort for code formatting and flake8 for style guides.

+

How to start (Setup project and development instance)

+

To start with the development setup, make sure you go through all the steps in Installation Guide and properly installed it.

+

Please create a new branch based on the develop branch that contains the most recent changes. This is mandatory.

+

git checkout -b myfeature develop

+

Then we strongly suggest to configure pre-commit to force linters on every commits you perform:

+
# create virtualenv to host pre-commit installation
+python3 -m venv venv
+source venv/bin/activate
+# from the project base directory
+pip install pre-commit
+pre-commit install -c .github/.pre-commit-config.yaml
+
+

Remember that whenever you make changes, you need to rebuild the docker image to see the reflected changes.

+

NOTE about documentation:

+

If you made any changes to an existing model/serializer/view, please run the following command to generate a new version of the API schema and docs:

+
docker exec -it greedybear_uwsgi python manage.py spectacular --file docs/source/schema.yml && make html
+
+

Frontend

+

To start the frontend in "develop" mode, you can execute the startup npm script within the folder frontend:

+
cd frontend/
+# Install
+npm i
+# Start
+DANGEROUSLY_DISABLE_HOST_CHECK=true npm start
+# See https://create-react-app.dev/docs/proxying-api-requests-in-development/#invalid-host-header-errors-after-configuring-proxy for why we use that flag in development mode
+
+

Most of the time you would need to test the changes you made together with the backend. In that case, you would need to run the backend locally too:

+
docker-compose up
+
+

Certego-UI

+

The GreedyBear Frontend is tightly linked to the certego-ui library. Most of the React components are imported from there. Because of this, it may happen that, during development, you would need to work on that library too. +To install the certego-ui library, please take a look to npm link and remember to start certego-ui without installing peer dependencies (to avoid conflicts with GreedyBear dependencies):

+
git clone https://github.com/certego/certego-ui.git
+# change directory to the folder where you have the cloned the library
+cd certego-ui/
+# install, without peer deps (to use packages of GreedyBear)
+npm i --legacy-peer-deps
+# create link to the project (this will globally install this package)
+sudo npm link
+# compile the library
+npm start
+
+

Then, open another command line tab, create a link in the frontend to the certego-ui and re-install and re-start the frontend application (see previous section):

+
cd frontend/
+npm link @certego/certego-ui
+
+

This trick will allow you to see reflected every changes you make in the certego-ui directly in the running frontend application.

+
Example application
+

The certego-ui application comes with an example project that showcases the components that you can re-use and import to other projects, like GreedyBear:

+
# To have the Example application working correctly, be sure to have installed `certego-ui` *without* the `--legacy-peer-deps` option and having it started in another command line
+cd certego-ui/
+npm i
+npm start
+# go to another tab
+cd certego-ui/example/
+npm i
+npm start
+
+

Create a pull request

+

Remember!!!

+

Please create pull requests only for the branch develop. That code will be pushed to master only on a new release.

+

Also remember to pull the most recent changes available in the develop branch before submitting your PR. If your PR has merge conflicts caused by this behavior, it won't be accepted.

+

Tests

+

Backend

+
Install testing requirements
+

You have to install pre-commit to have your code adjusted and fixed with the available linters:

+
pip install pre-commit
+pre-commit install -c .github/.pre-commit-config.yaml
+
+

Once done that, you won't have to think about linters anymore.

+
Run all tests
+
docker exec greedybear_uwsgi python3 manage.py test
+
+

Frontend

+

All the frontend tests must be run from the folder frontend. +The tests can contain log messages, you can suppress then with the environment variable SUPPRESS_JEST_LOG=True.

+
Run all tests
+
npm test
+
+
Run a specific component tests
+
npm test -- -t <componentPath>
+// example
+npm test tests/components/auth/Login.test.jsx
+
+
Run a specific test
+
npm test -- -t '<describeString> <testString>'
+// example
+npm test -- -t "Login component User login"
+
+

if you get any errors, fix them. +Once you make sure that everything is working fine, please squash all of our commits into a single one and finally create a pull request.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/GreedyBear/Installation/index.html b/GreedyBear/Installation/index.html new file mode 100644 index 0000000..cffb1d4 --- /dev/null +++ b/GreedyBear/Installation/index.html @@ -0,0 +1,838 @@ + + + + + + + + + + + + + +Installation - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Installation

+

Start by cloning the project

+
# clone the Greedybear project repository
+git clone https://github.com/honeynet/GreedyBear
+cd GreedyBear/
+
+# construct environment files from templates
+cp .env_template .env
+cd docker/
+cp env_file_template env_file
+cp env_file_postgres_template env_file_postgres
+cd ..
+cp frontend/public/env_template.js frontend/public/env.js
+
+

Now you can start by building the image using docker-compose and run the project.

+
# build the image locally
+docker-compose build
+
+# start the app
+docker-compose up
+
+# now the app is running on http://localhost:80
+
+# shut down the application
+docker-compose down
+
+

Note: To create a superuser run the following:

+
docker exec -ti greedybear_uwsgi python3 manage.py createsuperuser
+
+

The app administrator can enable/disable the extraction of source IPs for specific honeypots from the Django Admin. +This is used for honeypots that are not specifically implemented to extract additional information (so not Log4Pot and Cowrie).

+

Note that GreedyBear needs a running instance of ElasticSearch of a TPoT to function. +If you don't have one, you can make the following changes to make GreeyBear spin up it's own ElasticSearch and Kibana instances. +(...Care! This option would require enough RAM to run the additional containers. Suggested is >=16GB):

+
    +
  1. In docker/env_file, set the variable ELASTIC_ENDPOINT to http://elasticsearch:9200.
  2. +
  3. Add :docker/elasticsearch.yml to the last defined COMPOSE_FILE variable or uncomment the # local development with elasticsearch container block in .env file.
  4. +
+

Environment configuration

+

In the env_file, configure different variables as explained below.

+

Required variable to set:

+
    +
  • DEFAULT_FROM_EMAIL: email address used for automated correspondence from the site manager (example: noreply@mydomain.com)
  • +
  • DEFAULT_EMAIL: email address used for correspondence with users (example: info@mydomain.com)
  • +
  • EMAIL_HOST: the host to use for sending email with SMTP
  • +
  • EMAIL_HOST_USER: username to use for the SMTP server defined in EMAIL_HOST
  • +
  • EMAIL_HOST_PASSWORD: password to use for the SMTP server defined in EMAIL_HOST. This setting is used in conjunction with EMAIL_HOST_USER when authenticating to the SMTP server.
  • +
  • EMAIL_PORT: port to use for the SMTP server defined in EMAIL_HOST.
  • +
  • EMAIL_USE_TLS: whether to use an explicit TLS (secure) connection when talking to the SMTP server, generally used on port 587.
  • +
  • EMAIL_USE_SSL: whether to use an implicit TLS (secure) connection when talking to the SMTP server, generally used on port 465.
  • +
+

Optional configuration:

+
    +
  • SLACK_TOKEN: Slack token of your Slack application that will be used to send/receive notifications
  • +
  • DEFAULT_SLACK_CHANNEL: ID of the Slack channel you want to post the message to
  • +
+

Recaptcha configuration

+

The Registration Page and the Login Page contain a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown. +If your intention is to publish GreedyBear as a Service you should configure different variables as explained below.

+

In the frontend/public/env.js set the variable:

+
    +
  • RECAPTCHA_SITEKEY: Recaptcha Key for your site
  • +
+

In the docker/env_file set the variables:

+
    +
  • RECAPTCHA_SECRET_KEY_GB_LOCAL: your recaptcha secret key internal deployment
  • +
  • RECAPTCHA_SECRET_KEY_GB_PUBLIC: your recaptcha secret key for public deployment
  • +
+

In that case, you would need to re-build the application to have the changes properly reflected.

+

Update and Re-build

+

Rebuilding the project / Creating custom docker build

+

If you make some code changes and you like to rebuild the project, follow these steps:

+
    +
  1. Be sure that your .env file has a COMPOSE_FILE variable which mounts the docker/local.override.yml compose file.
  2. +
  3. docker-compose build to build the new docker image.
  4. +
  5. Start the containers with docker-compose up.
  6. +
+

Update to the most recent version

+

To update the project with the most recent available code you have to follow these steps:

+
$ cd <your_greedy_bear_directory> # go into the project directory
+$ git pull # pull new repository changes
+$ docker pull intelowlproject/greedybear:prod # pull new docker images
+$ docker-compose down # stop and destroy the currently running GreedyBear containers
+$ docker-compose up # restart the GreedyBear application
+
+

Installer for TPot Instance

+

The file 'installer_on_tpot.sh' allows the automatic installation of Greedybear on an existing TPot instance. +You can choose the type of Greedybear you want to install (http, https or local). +The installer will either clone Greedybear to '/opt/GreedyBear' or if Greedybear exists on your system you need to input the absolute path to the existing Greedybear folder. +It will prompt you for the necessary information/secrets needed.

+

Example: sudo ./installer.sh --type=http --folder=/opt/GreedyBear

+
+

Warning

+This installer is not officialy supported neither by Greedybear nor by TPOT maintainers. +It must be considered as a POC to have GB and TPOT installed in the same place. +Greedybear is supported to be executed only in a separate instance and to connect externally with the TPOTs. +
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/GreedyBear/Introduction/index.html b/GreedyBear/Introduction/index.html new file mode 100644 index 0000000..eff0900 --- /dev/null +++ b/GreedyBear/Introduction/index.html @@ -0,0 +1,664 @@ + + + + + + + + + + + + + +Introduction - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

GreedyBear Repository

+

Introduction

+

The project goal is to extract data of the attacks detected by a TPOT or a cluster of them and to generate some feeds that can be used to prevent and detect attacks.

+

Official announcement here.

+

Public feeds

+

There are public feeds provided by The Honeynet Project in this site: greedybear.honeynet.org. Example

+

To check all the available feeds, Please refer to our usage guide

+

Please do not perform too many requests to extract feeds or you will be banned.

+

If you want to be updated regularly, please download the feeds only once every 10 minutes (this is the time between each internal update).

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/GreedyBear/Usage/index.html b/GreedyBear/Usage/index.html new file mode 100644 index 0000000..82eb1c8 --- /dev/null +++ b/GreedyBear/Usage/index.html @@ -0,0 +1,803 @@ + + + + + + + + + + + + + +Usage.md - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ + + + + + +

Usage

+

User management

+

Registration

+

Since Greedybear v1.1.0 we added a Registration Page that can be used to manage Registration requests when providing GreedyBear as a Service.

+

After an user registration, an email is sent to the user to verify their email address. If necessary, there are buttons on the login page to resend the verification email and to reset the password.

+

Once the user has verified their email, they would be manually vetted before being allowed to use the GreedyBear platform. The registration requests would be handled in the Django Admin page by admins. +If you have GreedyBear deployed on an AWS instance you can use the SES service.

+

In a development environment the emails that would be sent are written to the standard output.

+

Recaptcha configuration

+

The Registration Page contains a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown. +If your intention is to publish GreedyBear as a Service you should configure the Recaptcha.

+

Amazon SES

+

If you like, you could use Amazon SES for sending automated emails.

+

First, you need to configure the environment variable AWS_SES to True to enable it. +Then you have to add some credentials for AWS: if you have GreedyBear deployed on the AWS infrastructure, you can use IAM credentials: +to allow that just set AWS_IAM_ACCESS to True. If that is not the case, you have to set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

+

Additionally, if you are not using the default AWS region of us-east-1, you need to specify your AWS_REGION. +You can customize the AWS Region location of you services by changing the environment variable AWS_REGION. Default is eu-central-1.

+

Feeds

+

GreedyBear is created with the aim to collect the information from the TPOTs and generate some actionable feeds, so that they can be easily accessible and act as valuable information to prevent and detect attacks.

+

The feeds are reachable through the following URL:

+
https://<greedybear_site>/api/feeds/<feed_type>/<attack_type>/<age>.<format>
+
+

The available feed_type are:

+
    +
  • log4j: attacks detected from the Log4pot.
  • +
  • cowrie: attacks detected from the Cowrie Honeypot.
  • +
  • all: get all types at once
  • +
  • The following honeypot feeds exist (for extraction of (only) the source IPs):
  • +
  • heralding
  • +
  • ciscoasa
  • +
  • honeytrap
  • +
  • dionaea
  • +
  • conpot
  • +
  • adbhoney
  • +
  • tanner
  • +
  • citrixhoneypot
  • +
  • mailoney
  • +
  • ipphoney
  • +
  • ddospot
  • +
  • elasticpot
  • +
  • dicompot
  • +
  • redishoneypot
  • +
  • sentrypeer
  • +
  • glutton
  • +
+

The available attack_type are:

+
    +
  • scanner: IP addresses captured by the honeypots while performing attacks
  • +
  • payload_request: IP addresses and domains extracted from payloads that would have been executed after a speficic attack would have been successful
  • +
  • all: get all types at once
  • +
+

The available age are:

+
    +
  • recent: most recent IOCs seen in the last 3 days
  • +
  • persistent: these IOCs are the ones that were seen regularly by the honeypots. This feeds will start empty once no prior data was collected and will become bigger over time.
  • +
+

The available formats are:

+
    +
  • txt: plain text (just one line for each IOC)
  • +
  • csv: CSV-like file (just one line for each IOC)
  • +
  • json: JSON file with additional information regarding the IOCs
  • +
+

Check the API specification or the to get all the details about how to use the available APIs.

+

Enrichment

+

GreedyBear provides an easy-to-query API to get the information available in GB regarding the queried observable (domain or IP address).

+
https://<greedybear_site>/api/enrichment?query=<observable>
+
+

This "Enrichment" API is protected through authentication. Please reach out Matteo Lodi or another member of The Honeynet Project if you are interested in gain access to this API.

+

If you would like to leverage this API without the need of writing even a line of code and together with a lot of other awesome tools, consider using IntelOwl.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Guide-docstrings/index.html b/Guide-docstrings/index.html new file mode 100644 index 0000000..30d2814 --- /dev/null +++ b/Guide-docstrings/index.html @@ -0,0 +1,910 @@ + + + + + + + + + + + + + +Docstrings guide - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Docstrings guide

+

Implementing Docstrings in Python Code

+

When you write or modify Python code in the codebase, it's important to add or update the docstrings accordingly. If you wish to display these docstrings in the documentation, follow these steps.

+

Suppose the docstrings are located in the following path: docs/Submodules/IntelOwl/api_app/analyzers_manager/classes, and you want to show the description of a class, such as BaseAnalyzerMixin.

+

To include this in the documentation, use the following command:

+
:::docs.Submodules.IntelOwl.api_app.analyzers_manager.classes.BaseAnalyzerMixin
+
+
+

Warning

+Make sure your path is correct and syntax is correct. +If you face any issues even path is correct then read the Submodules Guide. +
+

This is how it would look in documentation:

+
+ +
+

+ Bases: Plugin

+

Abstract Base class for Analyzers. +Never inherit from this branch, +always use either one of ObservableAnalyzer or FileAnalyzer classes.

+
+Source code in docs/Submodules/IntelOwl/api_app/analyzers_manager/classes.py +
class BaseAnalyzerMixin(Plugin, metaclass=ABCMeta):
+    """
+    Abstract Base class for Analyzers.
+    Never inherit from this branch,
+    always use either one of ObservableAnalyzer or FileAnalyzer classes.
+    """
+
+    HashChoices = HashChoices
+    ObservableTypes = ObservableTypes
+    TypeChoices = TypeChoices
+
+    @classmethod
+    @property
+    def config_exception(cls):
+        return AnalyzerConfigurationException
+
+    @property
+    def analyzer_name(self) -> str:
+        return self._config.name
+
+    @classmethod
+    @property
+    def report_model(cls):
+        return AnalyzerReport
+
+    @classmethod
+    @property
+    def config_model(cls):
+        return AnalyzerConfig
+
+    def get_exceptions_to_catch(self):
+        """
+        Returns additional exceptions to catch when running *start* fn
+        """
+        return (
+            AnalyzerConfigurationException,
+            AnalyzerRunException,
+        )
+
+    def _validate_result(self, result, level=0, max_recursion=190):
+        """
+        function to validate result, allowing to store inside postgres without errors.
+
+        If the character \u0000 is present in the string, postgres will throw an error
+
+        If an integer is bigger than max_int,
+        Mongodb is not capable to store and will throw an error.
+
+        If we have more than 200 recursion levels, every encoding
+        will throw a maximum_nested_object exception
+        """
+        if level == max_recursion:
+            logger.info(
+                f"We have reached max_recursion {max_recursion} level. "
+                f"The following object will be pruned {result} "
+            )
+            return None
+        if isinstance(result, dict):
+            for key, values in result.items():
+                result[key] = self._validate_result(
+                    values, level=level + 1, max_recursion=max_recursion
+                )
+        elif isinstance(result, list):
+            for i, _ in enumerate(result):
+                result[i] = self._validate_result(
+                    result[i], level=level + 1, max_recursion=max_recursion
+                )
+        elif isinstance(result, str):
+            return result.replace("\u0000", "")
+        elif isinstance(result, int) and result > 9223372036854775807:  # max int 8bytes
+            result = 9223372036854775807
+        return result
+
+    def after_run_success(self, content):
+        super().after_run_success(self._validate_result(content, max_recursion=15))
+
+
+
+
+

+get_exceptions_to_catch() +

+
+

Returns additional exceptions to catch when running start fn

+
+Source code in docs/Submodules/IntelOwl/api_app/analyzers_manager/classes.py +
57
+58
+59
+60
+61
+62
+63
+64
def get_exceptions_to_catch(self):
+    """
+    Returns additional exceptions to catch when running *start* fn
+    """
+    return (
+        AnalyzerConfigurationException,
+        AnalyzerRunException,
+    )
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Guide-documentation/index.html b/Guide-documentation/index.html new file mode 100644 index 0000000..483eaf1 --- /dev/null +++ b/Guide-documentation/index.html @@ -0,0 +1,757 @@ + + + + + + + + + + + + + +Documentation guide - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Setting Up the New Documentation Site Locally

+

To set up and run the documentation site on your local machine, please follow the steps below:

+

1. Create a Virtual Environment

+

To create a virtual environment named venv in your project directory, use the following command:

+
python3 -m venv venv
+
+

2. Activate the Virtual Environment

+

Activate the virtual environment to ensure that all dependencies are installed locally within your project directory.

+

On Linux/MacOS:

+
source venv/bin/activate
+
+

On Windows:

+
venv\Scripts\activate
+
+

3. Install Dependencies

+

To install all the necessary Python packages listed in requirements.txt, run:

+
pip install -r requirements.txt
+
+

Please run these commands to update and fetch the local Submodules.

+
git submodule foreach --recursive 'git fetch --all'
+git submodule update --init --remote --recursive --depth 1
+git submodule sync --recursive
+git submodule update --remote --recursive
+
+

4. Serve the Documentation Locally

+

Start a local development server to preview the documentation in your web browser. The server will automatically reload whenever you make changes to the documentation files.

+
mkdocs serve
+
+

5. Make Changes and Review

+

As you edit the documentation, you can view your changes in real-time through the local server. This step ensures everything looks as expected before deploying.

+

6. Push Changes to GitHub

+

Once you are satisfied with your changes, commit and push them to the GitHub repository. The documentation will be automatically deployed via GitHub Actions, making it live on the documentation site.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/advanced_configuration/index.html b/IntelOwl/advanced_configuration/index.html new file mode 100644 index 0000000..2829047 --- /dev/null +++ b/IntelOwl/advanced_configuration/index.html @@ -0,0 +1,1284 @@ + + + + + + + + + + + + + +Advanced Configuration - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Advanced Configuration

+

This page includes details about some advanced features that Intel Owl provides which can be optionally configured by the administrator.

+

ElasticSearch

+

Right now only ElasticSearch v8 is supported.

+

DSL

+

IntelOwl makes use of django-elasticsearch-dsl to index Job results into elasticsearch. The save and delete operations are auto-synced so you always have the latest data in ES.

+

In the env_file_app_template, you'd see various elasticsearch related environment variables. The user should spin their own Elastic Search instance and configure these variables.

+

Kibana

+

Intel Owl provides a Kibana's "Saved Object" configuration (with example dashboard and visualizations). It can be downloaded from here and can be imported into Kibana by going to the "Saved Objects" panel (http://localhost:5601/app/management/kibana/objects).

+

Example Configuration

+
    +
  1. Setup Elastic Search and Kibana and say it is running in a docker service with name elasticsearch on port 9200 which is exposed to the shared docker network. + (Alternatively, you can spin up a local Elastic Search instance, by appending --elastic to the ./start command. Note that the local Elastic Search instance consumes large amount of memory, and hence having >=16GB is recommended.))
  2. +
  3. In the env_file_app, we set ELASTICSEARCH_DSL_ENABLED to True and ELASTICSEARCH_DSL_HOST to elasticsearch:9200.
  4. +
  5. Now start the docker containers and execute
  6. +
+
docker exec -ti intelowl_uwsgi python manage.py search_index --rebuild
+
+

This will build and populate all existing job objects into the jobs index.

+

Business Intelligence

+

IntelOwl makes use of elasticsearch-py to store data that can be used for Business Intelligence purpose. +Since plugin reports are deleted periodically, this feature allows to save indefinitely small amount of data to keep track of how analyzers perform and user usage. +At the moment, the following information are sent to elastic:

+
    +
  • application name
  • +
  • timestamp
  • +
  • username
  • +
  • configuration used
  • +
  • process_time
  • +
  • status
  • +
  • end_time
  • +
  • parameters
  • +
+

Documents are saved in the ELEASTICSEARCH_BI_INDEX-%YEAR-%MONTH, allowing to manage the retention accordingly. +To activate this feature, it is necessary to set ELASTICSEARCH_BI_ENABLED to True in the env_file_app and +ELASTICSEARCH_BI_HOST to elasticsearch:9200 +or your elasticsearch server.

+

An index template is created after the first bulk submission of reports. +If you want to use kibana to visualize your data/make dashboard, you must create an index pattern: +Go to Kibana -> Discover -> Stack Management -> Index Patterns -> search for your index and use as time field timestamp

+

Authentication options

+

IntelOwl provides support for some of the most common authentication methods:

+ +

Google OAuth2

+

The first step is to create a Google Cloud Platform project, and then create OAuth credentials for it.

+

It is important to add the correct callback in the "Authorized redirect URIs" section to allow the application to redirect properly after the successful login. Add this:

+
http://<localhost|yourowndomain>/api/auth/google-callback
+
+

After that, specify the client ID and secret as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables and restart IntelOwl to see the applied changes.

+
+

Note

+While configuring Google Auth2 you can choose either to enable access to the all users with a Google Account ("External" mode) or to enable access to only the users of your organization ("Internal" mode). +Reference +
+

LDAP

+

IntelOwl leverages Django-auth-ldap to perform authentication via LDAP.

+

How to configure and enable LDAP on Intel Owl?

+
    +
  1. Change the values with your LDAP configuration inside configuration/ldap_config.py. This file is mounted as a docker volume, so you won't need to rebuild the image.
  2. +
+
+

Note

+For more details on how to configure this file, check the official documentation of the django-auth-ldap library. +
+
    +
  1. Once you have done that, you have to set the environment variable LDAP_ENABLED as True in the environment configuration file env_file_app. + Finally, you can restart the application with docker-compose up
  2. +
+

RADIUS Authentication

+

IntelOwl leverages Django-radius to perform authentication +via RADIUS server.

+

How to configure and enable RADIUS authentication on Intel Owl?

+
    +
  1. Change the values with your RADIUS auth configuration inside configuration/radius_config.py. This file is mounted as a + docker volume, so you won't need to rebuild the image.
  2. +
+
+

Note

+For more details on how to configure this file, check the official documentation of the django-radius library. +
+
    +
  1. Once you have done that, you have to set the environment variable RADIUS_AUTH_ENABLED as True in the environment + configuration file env_file_app. Finally, you can restart the application with docker-compose up
  2. +
+

OpenCTI

+

Like many other integrations that we have, we have an Analyzer and a Connector for the OpenCTI platform.

+

This allows the users to leverage these 2 popular open source projects and frameworks together.

+

So why we have a section here? This is because there are various compatibility problems with the official PyCTI library.

+

We found out (see issues in IntelOwl and PyCTI) that, most of the times, it is required that the OpenCTI version of the server you are using and the pycti version installed in IntelOwl must match perfectly.

+

Because of that, we decided to provide to the users the chance to customize the version of PyCTI installed in IntelOwl based on the OpenCTI version that they are using.

+

To do that, you would need to leverage the option --pycti-version provided by the ./start helper:

+
    +
  • check the default version that would be installed by checking the description of the option --pycti-version with ./start -h
  • +
  • if the default version is different from your OpenCTI server version, you need to rebuild the IntelOwl Image with ./start test build --pycti-version <your_version>
  • +
  • then restart the project ./start test up -- --build
  • +
  • enjoy
  • +
+

Cloud Support

+

AWS support

+

We have support for several AWS services.

+

You can customize the AWS Region location of you services by changing the environment variable AWS_REGION. Default is eu-central-1

+

You have to add some credentials for AWS: if you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials: +to allow that just set AWS_IAM_ACCESS to True. If that is not the case, you have to set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

+

S3

+

If you prefer to use S3 to store the analyzed samples, instead of the local storage, you can do it.

+

First, you need to configure the environment variable LOCAL_STORAGE to False to enable it and set AWS_STORAGE_BUCKET_NAME to the AWS bucket you want to use.

+

Then you need to configure permission access to the chosen S3 bucket.

+

Message Broker

+

IntelOwl at the moment supports 3 different message brokers:

+
    +
  • Redis (default)
  • +
  • RabbitMQ
  • +
  • Aws SQS
  • +
+

The default broker, if nothing is specified, is Redis.

+

To use RabbitMQ, you must use the option --rabbitmq when launching IntelOwl with the ./start script.

+

To use Aws SQS, you must use the option --sqs when launching IntelOwl with the .start script. +In that case, you should create new SQS queues in AWS called intelowl-<environment>-<queue_name> and give your instances on AWS the proper permissions to access it. +Moreover, you must populate the AWS_USER_NUMBER. This is required to connect in the right way to the selected SQS queues. +Only FIFO queues are supported.

+

If you want to use a remote message broker (like an ElasticCache or AmazonMQ instance), you must populate the BROKER_URL environment variable.

+

It is possible to use task priority inside IntelOwl: each User has default priority of 10, and robots users (like the Ingestors) have a priority of 7.
+You can customize these priorities inside Django Admin, in the Authentication.User Profiles section.

+

Websockets

+

Redis is used for two different functions:

+
    +
  • message broker
  • +
  • websockets
  • +
+

For this reason, a Redis instance is mandatory. +You can personalize IntelOwl in two different way:

+
    +
  • with a local Redis instance.
  • +
+

This is the default behaviour.

+
    +
  • With a remote Redis instance.
  • +
+

You must use the option --use-external-redis when launching IntelOwl with the .start script. +Moreover, you need to populate the WEBSOCKETS_URL environment variable. If you are using Redis as a message broker too, remember to populate the BROKER_URL environment variable

+

RDS

+

If you like, you could use AWS RDS instead of PostgreSQL for your database. In that case, you should change the database required options accordingly: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD and setup your machine to access the service.

+

If you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials to access the Postgres DB. +To allow that just set AWS_RDS_IAM_ROLE to True. In this case DB_PASSWORD is not required anymore.

+

Moreover, to avoid to run PostgreSQL locally, you would need to use the option --use-external-database when launching IntelOwl with the ./start script.

+

SES

+

If you like, you could use Amazon SES for sending automated emails (password resets / registration requests, etc).

+

You need to configure the environment variable AWS_SES to True to enable it.

+

Secrets

+

You can use the "Secrets Manager" to store your credentials. In this way your secrets would be better protected.

+

Instead of adding the variables to the environment file, you should just add them with the same name on the AWS Secrets Manager and Intel Owl will fetch them transparently.

+

Obviously, you should have created and managed the permissions in AWS in advance and accordingly to your infrastructure requirements.

+

Also, you need to set the environment variable AWS_SECRETS to True to enable this mode.

+

NFS

+

You can use a Network File System for the shared_files that are downloaded runtime by IntelOwl (for example Yara rules).

+

To use this feature, you would need to add the address of the remote file system inside the .env file, +and you would need to use the option --nfs when launching IntelOwl with the ./start script.

+

Google Kubernetes Engine

+

Right now there is no official support for Kubernetes deployments.

+

But we have an active community. Please refer to the following blog post for an example on how to deploy IntelOwl on Google Kubernetes Engine:

+

Deploying Intel-Owl on GKE by Mayank Malik.

+

Queues

+

Multi Queue

+

IntelOwl provides an additional multi-queue.override.yml compose file allowing IntelOwl users to better scale with the performance of their own architecture.

+

If you want to leverage it, you should add the option --multi-queue when starting the project. Example:

+
./start prod up --multi-queue
+
+

This functionality is not enabled by default because this deployment would start 2 more containers so the resource consumption is higher. We suggest to use this option only when leveraging IntelOwl massively.

+

Queue Customization

+

It is possible to define new celery workers: each requires the addition of a new container in the docker-compose file, as shown in the multi-queue.override.yml.

+

Moreover IntelOwl requires that the name of the workers are provided in the docker-compose file. This is done through the environment variable CELERY_QUEUES inside the uwsgi container. Each queue must be separated using the character ,, as shown in the example.

+

One can customize what analyzer should use what queue by specifying so in the analyzer entry in the analyzer_config.json configuration file. If no queue(s) are provided, the default queue will be selected.

+

Queue monitoring

+

IntelOwl provides an additional flower.override.yml compose file allowing IntelOwl users to use Flower features to monitor and manage queues and tasks

+

If you want to leverage it, you should add the option --flower when starting the project. Example:

+
./start prod up --flower
+
+

The flower interface is available at port 5555: to set the credentials for its access, update the environment variables

+
FLOWER_USER
+FLOWER_PWD
+
+

or change the .htpasswd file that is created in the docker directory in the intelowl_flower container.

+

Manual Usage

+

The ./start script essentially acts as a wrapper over Docker Compose, performing additional checks. +IntelOwl can still be started by using the standard docker compose command, but all the dependencies have to be manually installed by the user.

+

Options

+

The --project-directory and -p options are required to run the project. +Default values set by ./start script are "docker" and "intel_owl", respectively.

+

The startup is based on chaining various Docker Compose YAML files using -f option. +All Docker Compose files are stored in docker/ directory of the project. +The default compose file, named default.yml, requires configuration for an external database and message broker. +In their absence, the postgres.override.yml and rabbitmq.override.yml files should be chained to the default one.

+

The command composed, considering what is said above (using sudo), is

+
sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -p intel_owl up
+
+

The other most common compose file that can be used is for the testing environment. +The equivalent of running ./start test up is adding the test.override.yml file, resulting in:

+
sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -f docker/test.override.yml -p intel_owl up
+
+

All other options available in the ./start script (./start -h to view them) essentially chain other compose file to docker compose command with corresponding filenames.

+

Optional Analyzer

+

IntelOwl includes integrations with some analyzer that are not enabled by default. +These analyzers, stored under the integrations/ directory, are packed within Docker Compose files. +The compose.yml file has to be chained to include the analyzer. +The additional compose-test.yml file has to be chained for testing environment.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/advanced_usage/index.html b/IntelOwl/advanced_usage/index.html new file mode 100644 index 0000000..7094902 --- /dev/null +++ b/IntelOwl/advanced_usage/index.html @@ -0,0 +1,1111 @@ + + + + + + + + + + + + + +Advanced Usage - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Advanced Usage

+

This page includes details about some advanced features that Intel Owl provides which can be optionally enabled. Namely,

+

Organizations and User management

+

Starting from IntelOwl v4, a new "Organization" section is available on the GUI. This section substitute the previous permission management via Django Admin and aims to provide an easier way to manage users and visibility.

+

Multi Tenancy

+

Thanks to the "Organization" feature, IntelOwl can be used by multiple SOCs, companies, etc...very easily. +Right now it works very simply: only users in the same organization can see analysis of one another. An user can belong to an organization only.

+

Manage organizations

+

You can create a new organization by going to the "Organization" section, available under the Dropdown menu you cand find under the username.

+

Once you create an organization, you are the unique "Owner" of that organization. So you are the only one who can delete the organization and promote/demote/kick users. +Another role, which is called "Admin", can be set to a user (via the Django Admin interface only for now). +Owners and admins share the following powers: they can manage invitations and the organization's plugin configuration.

+

Accept Invites

+

Once an invite has sent, the invited user has to login, go to the "Organization" section and accept the invite there. Afterwards the Administrator will be able to see the user in his "Organization" section.

+

img.png

+

Plugins Params and Secrets

+

From IntelOwl v4.1.0, Plugin Parameters and Secrets can be defined at the organization level, in the dedicated section. +This allows to share configurations between users of the same org while allowing complete multi-tenancy of the application. +Only Owners and Admins of the organization can set, change and delete them.

+

Disable Plugins at Org level

+

The org admin can disable a specific plugin for all the users in a specific org. +To do that, Org Admins needs to go in the "Plugins" section and click the button "Enabled for organization" of the plugin that they want to disable.

+

img.png

+

Registration

+

Since IntelOwl v4.2.0 we added a Registration Page that can be used to manage Registration requests when providing IntelOwl as a Service.

+

After a user registration has been made, an email is sent to the user to verify their email address. If necessary, there are buttons on the login page to resend the verification email and to reset the password.

+

Once the user has verified their email, they would be manually vetted before being allowed to use the IntelOwl platform. The registration requests would be handled in the Django Admin page by admins. +If you have IntelOwl deployed on an AWS instance with an IAM role you can use the SES service.

+

To have the "Registration" page to work correctly, you must configure some variables before starting IntelOwl. See Optional Environment Configuration

+

In a development environment the emails that would be sent are written to the standard output.

+

Recaptcha configuration

+

The Registration Page contains a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown.

+

If your intention is to publish IntelOwl as a Service you should first remember to comply to the AGPL License.

+

Then you need to add the generated Recaptcha Secret in the RECAPTCHA_SECRET_KEY value in the env_file_app file.

+

Afterwards you should configure the Recaptcha Key for your site and add that value in the RECAPTCHA_SITEKEY in the frontend/public/env.js file. +In that case, you would need to re-build the application to have the changes properly reflected.

+

Optional Analyzers

+

Some analyzers which run in their own Docker containers are kept disabled by default. They are disabled by default to prevent accidentally starting too many containers and making your computer unresponsive.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAnalyzersDescription
Malware Tools Analyzers +
    +
  • PEframe_Scan
  • +
  • Capa_Info
  • +
  • Floss
  • +
  • Strings_Info
  • +
  • ClamAV
  • +
  • APKiD
  • +
  • Thug_URL_Info, + Thug_HTML_Info
  • +
  • BoxJS
  • +
  • Qiling_Windows, + Qiling_Windows_Shellcode, + Qiling_Linux, + Qiling_Linux_Shellcode
  • +
+
+
    +
  • PEFrame performs static analysis on Portable Executable malware and malicious MS Office documents
  • +
  • Capa detects capabilities in executable files
  • +
  • FLOSS automatically deobfuscate strings from malware binaries
  • +
  • String_Info_Classic extracts human-readable strings where as ML version of it ranks them
  • +
  • ClamAV antivirus engine scans files for trojans, viruses, malwares using a multi-threaded daemon
  • +
  • APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.
  • +
  • Thug performs hybrid dynamic/static analysis on a URL or HTML page.
  • +
  • Box-JS is a tool for studying JavaScript malware
  • +
  • Qiling is a tool for emulating the execution of a binary file or a shellcode. + It requires the configuration of its rootfs, and the optional configuration of profiles. + The rootfs can be copied from the Qiling project: please remember that Windows dll must be manually added for license reasons. + Qiling provides a DllCollector to retrieve dlls from your licensed Windows. + Profiles must be placed in the profiles subfolder +
  • +
+
TOR AnalyzersOnionscanScans TOR .onion domains for privacy leaks and information disclosures.
CyberChefCyberChefRun a transformation on a CyberChef server using pre-defined or custom recipes(rules that describe how the input has to be transformed). Check further instructions here
PCAP AnalyzersSuricataYou can upload a PCAP to have it analyzed by Suricata with the open Ruleset. The result will provide a list of the triggered signatures plus a more detailed report with all the raw data generated by Suricata. You can also add your own rules (See paragraph "Analyzers with special configuration"). The installation is optimized for scaling so the execution time is really fast.
PhoneInfogaPhoneInfoga_scanPhoneInfoga is one of the most advanced tools to scan international phone numbers. It allows you to first gather basic information such as country, area, carrier and line type, then use various techniques to try to find the VoIP provider or identify the owner. It works with a collection of scanners that must be configured in order for the tool to be effective. PhoneInfoga doesn't automate everything, it's just there to help investigating on phone numbers. here
+

To enable all the optional analyzers you can add the option --all_analyzers when starting the project. Example:

+
./start prod up --all_analyzers
+
+

Otherwise you can enable just one of the cited integration by using the related option. Example:

+
./start prod up --tor_analyzers
+
+

Customize analyzer execution

+

Some analyzers provide the chance to customize the performed analysis based on parameters that are different for each analyzer.

+
from the GUI
+

You can click on "Runtime Configuration" img.png button in the "Scan" page and add the runtime configuration in the form of a dictionary. +Example:

+
"VirusTotal_v3_File": {
+    "force_active_scan_if_old": true
+}
+
+
from Pyintelowl
+

While using send_observable_analysis_request or send_file_analysis_request endpoints, you can pass the parameter runtime_configuration with the optional values. +Example:

+
runtime_configuration = {
+    "Doc_Info": {
+        "additional_passwords_to_check": ["passwd", "2020"]
+    }
+}
+pyintelowl_client.send_file_analysis_request(..., runtime_configuration=runtime_configuration)
+
+

PhoneInfoga

+

PhoneInfoga provides several Scanners to extract as much information as possible from a given phone number. Those scanners may require authentication, so they're automatically skipped when no authentication credentials are found.

+

By default the scanner used is local. +Go through this guide to initiate other required API keys related to this analyzer.

+

CyberChef

+

You can either use pre-defined recipes or create your own as +explained here.

+

To use a pre-defined recipe, set the predefined_recipe_name argument to the name of the recipe as +defined here. Else, leave the predefined_recipe_name argument empty and set +the custom_recipe argument to the contents of +the recipe you want to +use.

+

Additionally, you can also (optionally) set the output_type argument.

+
Pre-defined recipes
+
    +
  • "to decimal": [{"op": "To Decimal", "args": ["Space", False]}]
  • +
+

Analyzers with special configuration

+

Some analyzers could require a special configuration:

+
    +
  • +

    GoogleWebRisk: this analyzer needs a service account key with the Google Cloud credentials to work properly. + You should follow the official guide for creating the key. + Then you can populate the secret service_account_json for that analyzer with the JSON of the service account file.

    +
  • +
  • +

    ClamAV: this Docker-based analyzer uses clamd daemon as its scanner and is communicating with clamdscan utility to scan files. The daemon requires 2 different configuration files: clamd.conf(daemon's config) and freshclam.conf (virus database updater's config). These files are mounted as docker volumes in /integrations/malware_tools_analyzers/clamav and hence, can be edited by the user as per needs, without restarting the application. Moreover ClamAV is integrated with unofficial open source signatures extracted with Fangfrisch. The configuration file fangfrisch.conf is mounted in the same directory and can be customized on your wish. For instance, you should change it if you want to integrate open source signatures from SecuriteInfo

    +
  • +
  • +

    Suricata: you can customize the behavior of Suricata:

    +
  • +
  • +

    /integrations/pcap_analyzers/config/suricata/rules: here there are Suricata rules. You can change the custom.rules files to add your own rules at any time. Once you made this change, you need to either restart IntelOwl or (this is faster) run a new analysis with the Suricata analyzer and set the parameter reload_rules to true.

    +
  • +
  • +

    /integrations/pcap_analyzers/config/suricata/etc: here there are Suricata configuration files. Change it based on your wish. Restart IntelOwl to see the changes applied.

    +
  • +
  • +

    Yara:

    +
  • +
  • You can customize both the repositories parameter and private_repositories secret to download and use different rules from the default that IntelOwl currently support.
      +
    • The repositories values is what will be used to actually run the analysis: if you have added private repositories, remember to add the url in repositories too!
    • +
    +
  • +
  • You can add local rules inside the directory at /opt/deploy/files_required/yara/YOUR_USERNAME/custom_rules/. Please remember that these rules are not synced in a cluster deploy: for this reason is advised to upload them on GitHub and use the repositories or private_repositories attributes.
  • +
+

Notifications

+

Since v4, IntelOwl integrated the notification system from the certego_saas package, allowing the admins to create notification that every user will be able to see.

+

The user would find the Notifications button on the top right of the page:

+

+

There the user can read notifications provided by either the administrators or the IntelOwl Maintainers.

+

As an Admin, if you want to add a notification to have it sent to all the users, you have to login to the Django Admin interface, go to the "Notifications" section and add it there. +While adding a new notification, in the body section it is possible to even use HTML syntax, allowing to embed images, links, etc; +in the app_name field, please remember to use intelowl as the app name.

+

Everytime a new release is installed, once the backend goes up it will automatically create a new notification, +having as content the latest changes described in the CHANGELOG.md, +allowing the users to keep track of the changes inside intelowl itself.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/api_docs/index.html b/IntelOwl/api_docs/index.html new file mode 100644 index 0000000..2d7f807 --- /dev/null +++ b/IntelOwl/api_docs/index.html @@ -0,0 +1,620 @@ + + + + + + + + + + + + + +API docs - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/api_docs/swagger-67f626ce.html b/IntelOwl/api_docs/swagger-67f626ce.html new file mode 100644 index 0000000..47f8534 --- /dev/null +++ b/IntelOwl/api_docs/swagger-67f626ce.html @@ -0,0 +1,125 @@ + + + + + + Swagger UI + + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/IntelOwl/assets/css/custom.css b/IntelOwl/assets/css/custom.css new file mode 100644 index 0000000..c4cccd4 --- /dev/null +++ b/IntelOwl/assets/css/custom.css @@ -0,0 +1,147 @@ +/* Color scheme */ + +[data-md-color-scheme="filigran"] { + /* Primary color shades */ + --md-primary-fg-color: #; + --md-primary-fg-color--light: #; + --md-primary-fg-color--dark: #; + --md-primary-bg-color: #; + --md-primary-bg-color--light: #; + + /* Accent color shades */ + --md-accent-fg-color: #1bb6ff; + --md-accent-fg-color--transparent: #; + --md-accent-bg-color: #; + --md-accent-bg-color--light: #; + + /* Default color shades */ + --md-default-fg-color: #00b1ff; + --md-default-fg-color--light: #697a94; + --md-default-fg-color--lighter: #3d4e66; + --md-default-fg-color--lightest: #03305d; + --md-default-bg-color: #070d19; + --md-default-bg-color--light: #0f1e38; + --md-default-bg-color--lighter: #152849; + --md-default-bg-color--lightest: #09101e; + + /* Code color shades */ + --md-code-fg-color: #ffffff; + --md-code-fg-color--light: #1bb6ff; + --md-code-bg-color: #001e3c; + --md-code-bg-color--light: #001e3c; + --md-code-bg-color--lighter: #001e3c; + + /* Code highlighting color shades */ + --md-code-hl-operator-color: #9ccc65; + --md-code-hl-punctuation-color: #cddc39; + --md-code-hl-comment-color: #3d4e66; + --md-code-hl-variable-color: #9ccc65; + --md-code-hl-name-color: #ffb300; + + /* Typeset color shades */ + --md-typeset-color: #ffffff; + + /* Typeset `a` color shades */ + --md-typeset-a-color: #0fbcff; + + /* Typeset `mark` color shades */ + --md-typeset-mark-color: #; + + /* Typeset `del` and `ins` color shades */ + --md-typeset-del-color: #; + --md-typeset-ins-color: #; + + /* Typeset `kbd` color shades */ + --md-typeset-kbd-color: #; + --md-typeset-kbd-accent-color: #; + --md-typeset-kbd-border-color: #; + + /* Typeset `table` color shades */ + --md-typeset-table-color: #0082d1; + --md-typeset-table-color--light: #051629; + + /* Admonition color shades */ + --md-admonition-fg-color: #ffffff; + --md-admonition-bg-color: var(--md-default-bg-color); + + /* Warning color shades */ + --md-warning-fg-color: #; + --md-warning-bg-color: #; + + /* Footer color shades */ + --md-footer-fg-color: #; + --md-footer-fg-color--light: #; + --md-footer-fg-color--lighter: #; + --md-footer-bg-color: #011222; + --md-footer-bg-color--dark: #09101e; +} + +/* Header */ +.md-header, +.md-tabs { + background-color: #09101e !important; +} + +/* Cards */ +.md-typeset .grid.cards > ol > li, +.md-typeset .grid.cards > ul > li, +.md-typeset .grid > .card { + border: none !important; + background-color: #001e3c !important; + border-radius: 10px; +} + +.md-typeset .grid.cards > ol > li:focus-within, +.md-typeset .grid.cards > ol > li:hover, +.md-typeset .grid.cards > ul > li:focus-within, +.md-typeset .grid.cards > ul > li:hover, +.md-typeset .grid > .card:focus-within, +.md-typeset .grid > .card:hover { + border: none !important; + background-color: #001e3c !important; + border-radius: 10px; +} + +html .md-footer-meta.md-typeset a:focus, +html .md-footer-meta.md-typeset a:hover { + color: #0fbcff !important; +} + +.middle { + margin: -5px 5px 0 0 !important; +} + +.md-header__button.md-logo img, +.md-header__button.md-logo svg { + height: 1.5rem; +} + +.grid { + column-gap: 0.8rem !important; +} + +.md-typeset__table { + min-width: 100%; +} + +.md-typeset table:not([class]) { + display: table; +} + +.md-typeset__table td, +th { + white-space: nowrap; +} + +/* Works on Chrome, Edge, and Safari */ +html, +body { + scrollbar-color: #070d19 #0f1e38; + scrollbarwidth: thin; + webkitfontsmoothing: auto; +} + +.glightbox img { + border: 1px solid #273d5f; + border-radius: 4.8px; +} diff --git a/IntelOwl/contribute/index.html b/IntelOwl/contribute/index.html new file mode 100644 index 0000000..fe6167b --- /dev/null +++ b/IntelOwl/contribute/index.html @@ -0,0 +1,1933 @@ + + + + + + + + + + + + + +Contribute - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Contribute

+

There are a lot of different ways you could choose to contribute to the IntelOwl Project:

+ +

Rules

+

Intel Owl welcomes contributors from anywhere and from any kind of education or skill level. We strive to create a community of developers that is welcoming, friendly and right.

+

For this reason it is important to follow some easy rules based on a simple but important concept: Respect.

+
    +
  • +

    Before asking any questions regarding how the project works, please read through all the documentation and install the project on your own local machine to try it and understand how it basically works. This is a form of respect to the maintainers.

    +
  • +
  • +

    DO NOT contact the maintainers with direct messages unless it is an urgent request. We don't have much time and cannot just answer to all the questions that we receive like "Guide me please! Help me understand how the project work". There is plenty of documentation and a lot of people in the community that can help you and would benefit from your questions. Share your problems and your knowledge. Please ask your questions in open channels (Github and Slack). This is a form of respect to the maintainers and to the community.

    +
  • +
  • +

    Before starting to work on an issue, you need to get the approval of one of the maintainers. Therefore please ask to be assigned to an issue. If you do not that but you still raise a PR for that issue, your PR can be rejected. This is a form of respect for both the maintainers and the other contributors who could have already started to work on the same problem.

    +
  • +
  • +

    When you ask to be assigned to an issue, it means that you are ready to work on it. When you get assigned, take the lock and then you disappear, you are not respecting the maintainers and the other contributors who could be able to work on that. So, after having been assigned, you have a week of time to deliver your first draft PR. After that time has passed without any notice, you will be unassigned.

    +
  • +
  • +

    Once you started working on an issue and you have some work to share and discuss with us, please raise a draft PR early with incomplete changes. This way you can continue working on the same and we can track your progress and actively review and help. This is a form of respect to you and to the maintainers.

    +
  • +
  • +

    When creating a PR, please read through the sections that you will find in the PR template and compile it appropriately. If you do not, your PR can be rejected. This is a form of respect to the maintainers.

    +
  • +
+

Code Style

+

Keeping to a consistent code style throughout the project makes it easier to contribute and collaborate. We make use of psf/black and isort for code formatting and flake8 for style guides.

+

How to start (Setup project and development instance)

+

This guide assumes that you have already performed the steps required to install the project. If not, please do it (Installation Guide).

+

Create a personal fork of the project on Github. +Then, please create a new branch based on the develop branch that contains the most recent changes. This is mandatory.

+

git checkout -b myfeature develop

+

Then we strongly suggest to configure pre-commit to force linters on every commits you perform

+
# From the project directory
+python3 -m venv venv
+source venv/bin/activate
+# from the project base directory
+pip install pre-commit
+pre-commit install
+
+# create .env file for controlling repo_downloader.sh
+# (to speed up image builds during development: it avoid downloading some repos)
+cp docker/.env.start.test.template docker/.env.start.test
+
+# set STAGE env variable to "local"
+sed -i "s/STAGE=\"production\"/STAGE=\"local\"/g" docker/env_file_app
+
+

Backend

+

Now, you can execute IntelOwl in development mode by selecting the mode test while launching the startup script:

+
./start test up
+
+

Every time you perform a change, you should perform an operation to reflect the changes into the application:

+
    +
  • if you changed the python requirements, restart the application and re-build the images. This is the slowest process. You can always choose this way but it would waste a lot of time.
  • +
+
./start test down && ./start test up -- --build
+
+
    +
  • if you changed either analyzers, connectors, playbooks or anything that is executed asynchronously by the "celery" containers, you just need to restart the application because we leverage Docker bind volumes that will reflect the changes to the containers. This saves the time of the build
  • +
+
./start test down && ./start test up
+
+
    +
  • if you made changes to either the API or anything that is executed only by the application server, changes will be instantly reflected and you don't need to do anything. This is thanks to the Django Development server that is executed instead of uwsgi while using the test mode
  • +
+

NOTE about documentation:

+

If you made any changes to an existing model/serializer/view, please run the following command to generate a new version of the API schema and docs:

+
docker exec -it intelowl_uwsgi python manage.py spectacular --file docs/source/schema.yml && make html
+
+

Frontend

+

To start the frontend in "develop" mode, you can execute the startup npm script within the folder frontend:

+
cd frontend/
+# Install
+npm i
+# Start
+DANGEROUSLY_DISABLE_HOST_CHECK=true npm start
+# See https://create-react-app.dev/docs/proxying-api-requests-in-development/#invalid-host-header-errors-after-configuring-proxy for why we use that flag in development mode
+
+

Most of the time you would need to test the changes you made together with the backend. In that case, you would need to run the backend locally too:

+
./start prod up
+
+
+

Note

+
    +
  • Running prod would be faster because you would leverage the official images and you won't need to build the backend locally. In case you would need to test backend changes too at the same time, please use test and refer to the previous section of the documentation.
  • +
  • This works thanks to the directive proxy in the frontend/package.json configuration
  • +
  • It may happen that the backend build does not work due to incompatibility between the frontend version you are testing with the current complete IntelOwl version you are running. In those cases, considering that you don't need to build the frontend together with the backend because you are already testing it separately, we suggest to remove the first build step (the frontend part) from the main Dockerfile temporarily and build IntelOwl with only the backend. In this way there won't be conflict issues.
  • +
+
+

Certego-UI

+

The IntelOwl Frontend is tightly linked to the certego-ui library. Most of the React components are imported from there. Because of this, it may happen that, during development, you would need to work on that library too. +To install the certego-ui library, please take a look to npm link and remember to start certego-ui without installing peer dependencies (to avoid conflicts with IntelOwl dependencies):

+
git clone https://github.com/certego/certego-ui.git
+# change directory to the folder where you have the cloned the library
+cd certego-ui/
+# install, without peer deps (to use packages of IntelOwl)
+npm i --legacy-peer-deps
+# create link to the project (this will globally install this package)
+sudo npm link
+# compile the library
+npm start
+
+

Then, open another command line tab, create a link in the frontend to the certego-ui and re-install and re-start the frontend application (see previous section):

+
cd frontend/
+npm link @certego/certego-ui
+
+

This trick will allow you to see reflected every changes you make in the certego-ui directly in the running frontend application.

+
Example application
+

The certego-ui application comes with an example project that showcases the components that you can re-use and import to other projects, like IntelOwl:

+
# To have the Example application working correctly, be sure to have installed `certego-ui` *without* the `--legacy-peer-deps` option and having it started in another command line
+cd certego-ui/
+npm i
+npm start
+# go to another tab
+cd certego-ui/example/
+npm i
+npm start
+
+

How to add a new Plugin

+

IntelOwl was designed to ease the addition of new plugins. With a simple python script you can integrate your own engine or integrate an external service in a short time.

+

There are two possible cases:

+
    +
  1. You are creating an entirely new Plugin, meaning that you actually wrote python code
  2. +
  3. You are creating a new Configuration for some code that already exists.
  4. +
+

If you are doing the step number 2, you can skip this paragraph.

+

First, you need to create the python code that will be actually executed. You can easily take other plugins as example to write this. +Then, you have to create a Python Module model. You can do this in the Django Admin page: +You have to specify which type of Plugin you wrote, and its python module. Again, you can use as an example an already configured Python Module.

+

Some Python Module requires to update some part of its code in a schedule way: for example Yara requires to update the rule repositories, QuarkEngine to update its database and so on. +If the Python Module that you define need this type of behaviour, you have to configure two things:

+
    +
  • In the python code, you have to override a method called update and put the updating logic (see other plugins for examples) there.
  • +
  • In the model class, you have to add the update_schedule (crontab syntax) that define when the update should be executed.
  • +
+

Some Python Module requires further check to see if the service provider is able to answer requests; for example if you have done too many requests, or the website is currently down for maintenance and so on. +If the Python Module that you define need this type of behaviour, you have to configure two things:

+
    +
  • In the python code, you can override a method called health_check and put there the custom health check logic. As default, plugins will try to make an HTTP HEAD request to the configured url (the Plugin must have a url attribute).
  • +
  • In the model class, you have to add the health_check_schedule (crontab syntax) that define when the health check should be executed.
  • +
+

Press Save and continue editing to, at the moment, manually ad the Parameters that the python code requires (the class attributes that you needed):

+
    +
  1. *name: Name of the parameter that will be dynamically added to the python class (if is a secret, in the python code a _ wil be prepended to the name)
  2. +
  3. *type: data type, string, list, dict, integer, boolean, float
  4. +
  5. *description
  6. +
  7. *required: true or false, meaning that a value is necessary to allow the run of the analyzer
  8. +
  9. *is_secret: true or false
  10. +
+

At this point, you can follow the specific guide for each plugin

+

How to add a new Analyzer

+

You may want to look at a few existing examples to start to build a new one, such as:

+
    +
  • shodan.py, if you are creating an observable analyzer
  • +
  • malpedia_scan.py, if you are creating a file analyzer
  • +
  • peframe.py, if you are creating a docker based analyzer
  • +
  • Please note: If the new analyzer that you are adding is free for the user to use, please add it in the FREE_TO_USE_ANALYZERS playbook. To do this you have to make a migration file; you can use 0026_add_mmdb_analyzer_free_to_use as a template.
  • +
+

After having written the new python module, you have to remember to:

+
    +
  1. Put the module in the file_analyzers or observable_analyzers directory based on what it can analyze
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new analyzer. This is a trick to have tests in the same class of its analyzer.
  4. +
  5. Create the configuration inside django admin in Analyzers_manager/AnalyzerConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. *Type: observable or file
  18. +
  19. *Docker based: if the analyzer run through a docker instance
  20. +
  21. *Maximum tlp: maximum tlp to allow the run on the connector
  22. +
  23. ~Observable supported: required if type is observable
  24. +
  25. ~Supported filetypes: required if type is file and not supported filetypes is empty
  26. +
  27. Run hash: if the analyzer supports hash as inputs
  28. +
  29. ~Run hash type: required if run hash is True
  30. +
  31. ~Not supported filetypes: required if type is file and supported filetypes is empty
  32. +
+

Integrating a docker based analyzer

+

If the analyzer you wish to integrate doesn't exist as a public API or python package, it should be integrated with its own docker image +which can be queried from the main Django app.

+
    +
  • It should follow the same design principle as the other such existing integrations, unless there's very good reason not to.
  • +
  • The dockerfile should be placed at ./integrations/<analyzer_name>/Dockerfile.
  • +
  • Two docker-compose files compose.yml for production and compose-tests.yml for testing should be placed under ./integrations/<analyzer_name>.
  • +
  • If your docker-image uses any environment variables, add them in the docker/env_file_integrations_template.
  • +
  • Rest of the steps remain same as given under "How to add a new analyzer".
  • +
+

How to add a new Connector

+

You may want to look at a few existing examples to start to build a new one:

+ +

After having written the new python module, you have to remember to:

+
    +
  1. Put the module in the connectors directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new connector. This is a trick to have tests in the same class of its connector.
  4. +
  5. Create the configuration inside django admin in Connectors_manager/ConnectorConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. *Maximum tlp: maximum tlp to allow the run on the connector
  18. +
  19. *Run on failure: if the connector should be run even if the job fails
  20. +
+

How to add a new Ingestor

+
    +
  1. Put the module in the ingestors directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new ingestor. This is a trick to have tests in the same class of its ingestor.
  4. +
  5. Create the configuration inside django admin in Ingestors_manager/IngestorConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. *Playbook to Execute: Playbook that will be executed on every IOC retrieved
  18. +
  19. *Schedule: Crontab object that describes the schedule of the ingestor. You are able to create a new clicking the plus symbol.
  20. +
+

How to add a new Pivot

+
    +
  1. Put the module in the pivots directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new pivot. This is a trick to have tests in the same class of its pivot.
  4. +
  5. Create the configuration inside django admin in Pivots_manager/PivotConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. *Playbook to Execute: Playbook that will be executed in the Job generated by the Pivot
  18. +
+

Most of the times you don't need to create a new Pivot Module. There are already some base modules that can be extended. +The most important ones are the following 2: 1.AnyCompare: use this module if you want to create a custom Pivot from a specific value extracted from the results of the analyzers/connectors. How? you should populate the parameter field_to_compare with the dotted path to the field you would like to extract the value from. 2.SelfAnalyzable: use this module if you want to create a custom Pivot that would analyze again the same observable/file.

+

How to add a new Visualizer

+

Configuration

+
    +
  1. Put the module in the visualizers directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new visualizer. This is a trick to have tests in the same class of its visualizer.
  4. +
  5. Create the configuration inside django admin in Visualizers_manager/VisualizerConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Config:
      +
    1. *Queue: celery queue that will be used
    2. +
    3. *Soft_time_limit: maximum time for the task execution
    4. +
    +
  14. +
  15. *Playbook: Playbook that must have run to execute the visualizer
  16. +
+

Python class

+

The visualizers' python code could be not immediate, so a small digression on how it works is necessary. +Visualizers have as goal to create a data structure inside the Report that the frontend is able to parse and correctly visualize on the page. +To do so, some utility classes have been made:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ClassDescriptionVisual representation/example
VisualizablePageA single page of the final report, made of different levels. Each page added is represented as a new tab in frontend.Visualizable Page example
VisualizableLevel + Each level corresponds to a line in the final frontend visualizations. Every level is made of a + VisualizableHorizontalList. + The dimension of the level can be customized with the size parameter (1 is the biggest, 6 is the smallest). + Visualizable Level example
VisualizableHorizontalListAn horizontal list of visualizable elements. In the example there is an horizontal list of vertical lists.Visualizable Horizontal List Example
VisualizableVerticalListA vertical list made of a name, a title, and the list of elements.Visualizable Vertical List Example
VisualizableTableA table of visualizable elements. In the example there is a table of base and vertical lists.Visualizable Table Example
VisualizableBoolThe representation of a boolean value. It can be enabled or disabled with colors.Visualizable Bool example
VisualizableTitleThe representation of a tuple, composed of a title and a value.Visualizable Title example
VisualizableBaseThe representation of a base string. Can have a link attached to it and even an icon. The background color can be changed.The title above is composed by two `VisualizableBase`
+

Inside a Visualizer you can retrieve the reports of the analyzers and connectors that have been specified inside configuration of the Visualizer itself using .analyzer_reports() and .connector_reports(). +At this point, you can compose these values as you wish wrapping them with the Visualizable classes mentioned before.

+

The best way to create a visualizer is to define several methods, one for each Visualizable you want to show in the UI, in your new visualizer and decore them with visualizable_error_handler_with_params. This decorator handles exceptions: in case there is a bug during the generation of a Visualizable element, it will be show an error instead of this component and all the other Visualizable are safe and will render correctly. Be careful using it because is a function returning a decorator! This means you need to use a syntax like this:

+
@visualizable_error_handler_with_params(error_name="custom visualizable", error_size=VisualizableSize.S_2)
+def custom_visualizable(self):
+   ...
+
+

instead of the syntax of other decorators that doesn't need the function call.

+

You may want to look at a few existing examples to start to build a new one:

+ +

How to share your plugin with the community

+

To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration

+
    +
  1. You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/YOUR_PLUGIN_manager/migrations). The script will create the following models:
      +
    1. PythonModule
    2. +
    3. AnalyzerConfig
    4. +
    5. Parameter
    6. +
    7. PluginConfig
    8. +
    +
  2. +
  3. Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin AnalyzerConfig <new_analyzer_name>
  4. +
+

Add the new analyzer in the lists in the docs: Usage. Also, if the analyzer provides additional optional configuration, add the available options here: Advanced-Usage

+

In the Pull Request remember to provide some real world examples (screenshots and raw JSON results) of some successful executions of the analyzer to let us understand how it would work.

+

How to add a new Playbook

+
    +
  1. Create the configuration inside django admin in Playbooks_manager/PlaybookConfigs (* = mandatory, ~ = mandatory on conditions)
  2. +
  3. *Name: specific name of the configuration
  4. +
  5. *Description: description of the configuration
  6. +
  7. *Type: list of types that are supported by the playbook
  8. +
  9. *Analyzers: List of analyzers that will be run
  10. +
  11. *Connectors: List of connectors that will be run
  12. +
+

How to share your playbook with the community

+

To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration +You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/playbook_manager/migrations).

+

Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin PlaybookConfig <new_analyzer_name>

+

How to modify a plugin

+

If the changes that you have to make should stay local, you can just change the configuration inside the Django admin page.

+

But if, instead, you want your changes to be usable by every IntelOwl user, you have to create a new migration.

+

To do so, you can use the following snippets as an example:

+
    +
  1. You have to create a new migration file
  2. +
  3. Add as dependency the previous last migration of the package
  4. +
  5. You have to create a forward and a reverse function
  6. +
  7. You have to make the proper changes of the configuration inside these functions (change parameters, secrets, or even delete the configuration)
  8. +
  9. If changes are made, you have to validate the instance calling .full_clean() and then you can save the instance with .save()
  10. +
+

Example: how to add a new parameter in the configuration with a default value

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   PluginConfig = apps.get_model("api_app", "PluginConfig")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   p = Parameter(name="mynewfield", type="str", description="Test field", is_secret=False, required=True, python_module=pm)
+   p.full_clean()
+   p.save()
+   for connector in pm.connectorconfigs.all():
+    pc = PluginConfig(value="test", connector_config=connector, python_module=pm, for_organization=False, owner=None, parameter=p)
+    pc.full_clean()
+    pc.save()
+
+

Example: how to add a new secret in the configuration

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   p = Parameter(name="mynewsecret", type="str", description="Test field", is_secret=True, required=True, python_module=pm)
+   p.full_clean()
+   p.save()
+
+

Example: how to delete a parameter

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   Parameter.objects.get(name="myoldfield", python_module=pm).delete()
+
+

Example: how to change the default value of a parameter

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   PluginConfig = apps.get_model("api_app", "PluginConfig")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   p = Parameter.objects.get(name="myfield", python_module=pm)
+   PluginConfig.objects.filter(parameter=p, python_module=pm, for_organization=False, owner=None ).update(value="newvalue")
+
+

Modifying functionalities of the Certego packages

+

Since v4, IntelOwl leverages some packages from Certego:

+
    +
  • certego-saas that integrates some common reusable Django applications and tools that can be used for generic services.
  • +
  • certego-ui that contains reusable React components for the UI.
  • +
+

If you need to modify the behavior or add feature to those packages, please follow the same rules for IntelOwl and request a Pull Request there. The same maintainers of IntelOwl will answer to you.

+

Follow these guides to understand how to start to contribute to them while developing for IntelOwl:

+
    +
  • certego-saas: create a fork, commit your changes in your local repo, then change the commit hash to the last one you made in the requirements file. Ultimately re-build the project
  • +
  • certego-ui: Frontend doc
  • +
+

How to test the application

+

IntelOwl makes use of the django testing framework and the unittest library for unit testing of the API endpoints and End-to-End testing of the analyzers and connectors.

+

Configuration

+
    +
  • +

    In the encrypted folder tests/test_files.zip (password: "intelowl") there are some files that you can use for testing purposes.

    +
  • +
  • +

    With the following environment variables you can customize your tests:

    +
  • +
  • +

    DISABLE_LOGGING_TEST -> disable logging to get a clear output

    +
  • +
  • +

    MOCK_CONNECTIONS -> mock connections to external API to test the analyzers without a real connection or a valid API key

    +
  • +
  • +

    If you prefer to use custom inputs for tests, you can change the following environment variables in the environment file based on the data you would like to test:

    +
  • +
  • TEST_MD5
  • +
  • TEST_URL
  • +
  • TEST_IP
  • +
  • TEST_DOMAIN
  • +
+

Setup containers

+

The point here is to launch the code in your environment and not the last official image in Docker Hub. +For this, use the test or the ci option when launching the containers with the ./start script.

+
    +
  • Use the test option to actually execute tests that simulate a real world environment without mocking connections.
  • +
  • Use the ci option to execute tests in a CI environment where connections are mocked.
  • +
+
$ ./start test up
+$ # which corresponds to the command: docker-compose -f docker/default.yml -f docker/test.override.yml up
+
+

Launch tests

+

Now that the containers are up, we can launch the test suite.

+

Backend

+
Run all tests
+

Examples:

+
$ docker exec intelowl_uwsgi python3 manage.py test
+
+
Run tests for a particular plugin
+

To test a plugin in real environment, i.e. without mocked data, we suggest that you use the GUI of IntelOwl directly. +Meaning that you have your plugin configured, you have selected a correct observable/file to analyze, +and the final report shown in the GUI of IntelOwl is exactly what you wanted.

+
Run tests available in a particular file
+

Examples:

+
$ docker exec intelowl_uwsgi python3 manage.py test tests.api_app tests.test_crons # dotted paths
+
+

Frontend

+

All the frontend tests must be run from the folder frontend. +The tests can contain log messages, you can suppress then with the environment variable SUPPRESS_JEST_LOG=True.

+
Run all tests
+
npm test
+
+
Run a specific component tests
+
npm test -- -t <componentPath>
+// example
+npm test tests/components/auth/Login.test.jsx
+
+
Run a specific test
+
npm test -- -t '<describeString> <testString>'
+// example
+npm test -- -t "Login component User login"
+
+

Create a pull request

+

Remember!!!

+

Please create pull requests only for the branch develop. That code will be pushed to master only on a new release.

+

Also remember to pull the most recent changes available in the develop branch before submitting your PR. If your PR has merge conflicts caused by this behavior, it won't be accepted.

+

Install testing requirements

+

Run pip install -r requirements/test-requirements.txt to install the requirements to validate your code.

+

Pass linting and tests

+
    +
  1. Run psf/black to lint the files automatically, then flake8 to check and isort:
  2. +
+

(if you installed pre-commit this is performed automatically at every commit)

+
$ black . --exclude "migrations|venv"
+$ flake8 . --show-source --statistics
+$ isort . --profile black --filter-files --skip venv
+
+

if flake8 shows any errors, fix them.

+
    +
  1. Run the build and start the app using the docker-compose test file. In this way, you would launch the code in your environment and not the last official image in Docker Hub:
  2. +
+
$ ./start ci build
+$ ./start ci up
+
+
    +
  1. Here, we simulate the GitHub CI tests locally by running the following 3 tests:
  2. +
+
$ docker exec -ti intelowl_uwsgi unzip -P intelowl tests/test_files.zip -d test_files
+$ docker exec -ti intelowl_uwsgi python manage.py test tests
+
+
+

Note: IntelOwl has dynamic testing suite. This means that no explicit analyzers/connector tests are required after the addition of a new analyzer or connector.

+
+

If everything is working, before submitting your pull request, please squash your commits into a single one!

+

How to squash commits to a single one

+
    +
  • Run git rebase -i HEAD~[NUMBER OF COMMITS]
  • +
  • You should see a list of commits, each commit starting with the word "pick".
  • +
  • Make sure the first commit says "pick" and change the rest from "pick" to "squash". -- This will squash each commit into the previous commit, which will continue until every commit is squashed into the first commit.
  • +
  • Save and close the editor.
  • +
  • It will give you the opportunity to change the commit message.
  • +
  • Save and close the editor again.
  • +
  • Then you have to force push the final, squashed commit: git push --force-with-lease origin.
  • +
+

Squashing commits can be a tricky process but once you figure it out, it's really helpful and keeps our repo concise and clean.

+

Debug application problems

+

Keep in mind that, if any errors arise during development, you would need to check the application logs to better understand what is happening so you can easily address the problem.

+

This is the reason why it is important to add tons of logs in the application...if they are not available in time of needs you would cry really a lot.

+

Where are IntelOwl logs? +With a default installation of IntelOwl, you would be able to get the application data from the following paths in your OS:

+
    +
  • /var/lib/docker/volumes/intel_owl_generic_logs/_data/django: Django Application logs
  • +
  • /var/lib/docker/volumes/intel_owl_generic_logs/_data/uwsgi: Uwsgi application server logs
  • +
  • /var/lib/docker/volumes/intel_owl_nginx_logs/_data/: Nginx Web Server Logs
  • +
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/index.html b/IntelOwl/index.html new file mode 100644 index 0000000..27b0016 --- /dev/null +++ b/IntelOwl/index.html @@ -0,0 +1,589 @@ + + + + + + + + + + + +Index - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Index

+

This is a Documentation for IntelOwl.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/installation/index.html b/IntelOwl/installation/index.html new file mode 100644 index 0000000..d175e4e --- /dev/null +++ b/IntelOwl/installation/index.html @@ -0,0 +1,1392 @@ + + + + + + + + + + + + + +Installation - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Installation

+

Requirements

+

The project leverages docker compose with a custom Bash script and you need to have the following packages installed in your machine:

+ +

In some systems you could find pre-installed older versions. Please check this and install a supported version before attempting the installation. Otherwise it would fail. +Note: We've added a new Bash script initialize.sh that will check compatibility with your system and attempt to install the required dependencies.

+
+

Note

+
    +
  • The project uses public docker images that are available on Docker Hub
  • +
  • IntelOwl is tested and supported to work in a Debian distro. More precisely we suggest using Ubuntu. Other Linux-based OS should work but that has not been tested much. It may also run on Windows, but that is not officially supported.
  • +
  • IntelOwl does not support ARM at the moment. We'll fix this with the next v6.0.5 release +
  • Before installing remember that you must comply with the LICENSE and the Legal Terms
  • +
+
+
+

Warning

+The start script requires a `bash` version > 4 to run. + +Note that macOS is shipped with an older version of bash. Please ensure to upgrade before running the script. + +
+

TL;DR

+

Obviously we strongly suggest reading through all the page to configure IntelOwl in the most appropriate way.

+

However, if you feel lazy, you could just install and test IntelOwl with the following steps. +docker will be run with sudo if permissions/roles have not been set.

+
# clone the IntelOwl project repository
+git clone https://github.com/intelowlproject/IntelOwl
+cd IntelOwl/
+
+# run helper script to verify installed dependencies and configure basic stuff
+./initialize.sh
+
+# start the app
+./start prod up
+# now the application is running on http://localhost:80
+
+# create a super user
+sudo docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser
+
+# now you can login with the created user from http://localhost:80/login
+
+# Have fun!
+
+
+

Warning

+The first time you start IntelOwl, a lot of database migrations are being applied. This requires some time. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page. +
+

Infrastructure Requirements

+

These are our recommendations for dedicated deployments of IntelOwl: +- Basic Installation in a VM: 2 CPU, 4GB RAM, 20GB Disk +- Intensive Usage (hundreds of analysis in a hour) in a single VM: 8CPU, 16GB RAM and 80GB Disk.

+

Please remember that every environment has its own peculiarities so these numbers must not be taken as the holy grail.

+

What should be done is a comprehensive evaluation of the environment where the application will deployed.

+

For more complex environments, a Docker Swarm / Kubernetes cluster is recommended.

+

IntelOwl's maintainers are available to offer paid consultancy and mentorship about that.

+

Deployment Components

+

IntelOwl is composed of various different technologies, namely:

+
    +
  • React: Frontend, using CRA and certego-ui
  • +
  • Django: Backend
  • +
  • PostgreSQL: Database
  • +
  • Redis: Message Broker
  • +
  • Celery: Task Queue
  • +
  • Nginx: Reverse proxy for the Django API and web asssets.
  • +
  • Uwsgi: Application Server
  • +
  • Daphne: Asgi Server for WebSockets
  • +
  • Elastic Search (optional): Auto-sync indexing of analysis' results.
  • +
  • Kibana (optional): GUI for Elastic Search. We provide a saved configuration with dashboards and visualizations.
  • +
  • Flower (optional): Celery Management Web Interface
  • +
+

All these components are managed via docker compose.

+

Deployment Preparation

+ +

Open a terminal and execute below commands to construct new environment files from provided templates.

+
./initialize.sh
+
+

Environment configuration (required)

+

In the docker/env_file_app, configure different variables as explained below.

+

REQUIRED variables to run the image:

+
    +
  • DB_HOST, DB_PORT, DB_USER, DB_PASSWORD: PostgreSQL configuration (The DB credentals should match the ones in the env_file_postgres). If you like, you can configure the connection to an external PostgreSQL instance in the same variables. Then, to avoid to run PostgreSQL locally, please run IntelOwl with the option --use-external-database. Otherwise, DB_HOST must be postgres to have the app properly communicate with the PostgreSQL container.
  • +
  • DJANGO_SECRET: random 50 chars key, must be unique. If you do not provide one, Intel Owl will automatically set a secret key and use the same for each run. The key is generated by initialize.sh script.
  • +
+

Strongly recommended variable to set:

+
    +
  • INTELOWL_WEB_CLIENT_DOMAIN (example: localhost/mywebsite.com): the web domain of your instance, this is used for generating links to analysis results.
  • +
+

Optional configuration:

+
    +
  • OLD_JOBS_RETENTION_DAYS: Database retention for analysis results (default: 14 days). Change this if you want to keep your old analysis longer in the database.
  • +
+

Other optional configuration to enable specific services / features

+

Configuration required to enable integration with Slack:

+
    +
  • SLACK_TOKEN: Slack token of your Slack application that will be used to send/receive notifications
  • +
  • DEFAULT_SLACK_CHANNEL: ID of the Slack channel you want to post the message to
  • +
+

Configuration required to enable Re-Captcha in the Login and the Registration Page: +In the docker/env_file_app:

+
    +
  • USE_RECAPTCHA: if you want to use recaptcha on your login
  • +
  • RECAPTCHA_SECRET_KEY: your recaptcha secret key + In the frontend/public/env.js:
  • +
  • RECAPTCHA_SITEKEY: Recaptcha Key for your site
  • +
+

Configuration required to have InteOwl sending Emails (registration requests, mail verification, password reset/change, etc)

+
    +
  • DEFAULT_FROM_EMAIL: email address used for automated correspondence from the site manager (example: noreply@mydomain.com)
  • +
  • DEFAULT_EMAIL: email address used for correspondence with users (example: info@mydomain.com)
  • +
  • EMAIL_HOST: the host to use for sending email with SMTP
  • +
  • EMAIL_HOST_USER: username to use for the SMTP server defined in EMAIL_HOST
  • +
  • EMAIL_HOST_PASSWORD: password to use for the SMTP server defined in EMAIL_HOST. This setting is used in conjunction with EMAIL_HOST_USER when authenticating to the SMTP server.
  • +
  • EMAIL_PORT: port to use for the SMTP server defined in EMAIL_HOST.
  • +
  • EMAIL_USE_TLS: whether to use an explicit TLS (secure) connection when talking to the SMTP server, generally used on port 587.
  • +
  • EMAIL_USE_SSL: whether to use an implicit TLS (secure) connection when talking to the SMTP server, generally used on port 465.
  • +
+

Database configuration (required if running PostgreSQL locally)

+

If you use a local PostgreSQL instance (this is the default), in the env_file_postgres you have to configure different variables as explained below.

+

Required variables:

+
    +
  • POSTGRES_PASSWORD (same as DB_PASSWORD)
  • +
  • POSTGRES_USER (same as DB_USER)
  • +
  • POSTGRES_DB (default: intel_owl_db)
  • +
+ +

If you want to have your logs rotated correctly, we suggest you to add the configuration for the system Logrotate. +To do that you can leverage the initialize.sh script. Otherwise, if you have skipped that part, you can manually install logrotate by launching the following script:

+
cd ./docker/scripts
+./install_logrotate.sh
+
+

We decided to do not leverage Django Rotation Configuration because it caused problematic concurrency issues, leading to logs that are not rotated correctly and to apps that do not log anymore. +Logrotate configuration is more stable.

+ +

We added few Crontab configurations that could be installed in the host machine at system level to solve some possible edge-case issues:

+
    +
  • Memory leaks: Once a week it is suggested to do a full restart of the application to clean-up the memory used by the application. Practical experience suggest us to do that to solve some recurrent memory issues in Celery. A cron called application_restart has been added for this purpose (it uses the absolute path of start script in the container). This cron assumes that you have executed IntelOwl with the parameters --all_analyzers. If you didn't, feel free to change the cron as you wish.
  • +
+

This configuration is optional but strongly recommended for people who want to have a production grade deployment. To install it you need to run the following script in each deployed server:

+
cd ./docker/scripts
+./install_crontab.sh
+
+

Web server configuration (required for enabling HTTPS)

+

Intel Owl provides basic configuration for:

+
    +
  • Nginx (configuration/nginx/http.conf)
  • +
  • Uwsgi (configuration/intel_owl.ini)
  • +
+

In case you enable HTTPS, remember to set the environment variable HTTPS_ENABLED as "enabled" to increment the security of the application.

+

There are 3 options to execute the web server:

+
    +
  • HTTP only (default)
  • +
+

The project would use the default deployment configuration and HTTP only.

+
    +
  • HTTPS with your own certificate
  • +
+

The project provides a template file to configure Nginx to serve HTTPS: configuration/nginx/https.conf.

+

You should change ssl_certificate, ssl_certificate_key and server_name in that file and put those required files in the specified locations.

+

Then you should call the ./start script with the parameter --https to leverage the right Docker Compose file for HTTPS.

+

Plus, if you use Flower, you should change in the docker/flower.override.yml the flower_http.conf with flower_https.conf.

+
    +
  • HTTPS with Let's Encrypt
  • +
+

We provide a specific docker-compose file that leverages Traefik to allow fast deployments of public-faced and HTTPS-enabled applications.

+

Before using it, you should configure the configuration file docker/traefik.override.yml by changing the email address and the hostname where the application is served. For a detailed explanation follow the official documentation: Traefix doc.

+

After the configuration is done, you can add the option --traefik while executing ./start

+

Run

+
+

Important Info

+IntelOwl depends heavily on docker and docker compose so as to hide this complexity from the enduser the project +leverages a custom shell script (start) to interface with docker compose. + +You may invoke $ ./start --help to get help and usage info. + +The CLI provides the primitives to correctly build, run or stop the containers for IntelOwl. Therefore, + +
    +
  • It is possible to attach every optional docker container that IntelOwl has: +multi_queue with traefik enabled while every optional docker analyzer is active.
  • +
  • It is possible to insert an optional docker argument that the CLI will pass to docker-compose
  • +
+
+

Now that you have completed different configurations, starting the containers is as simple as invoking:

+
$ ./start prod up
+
+

You can add the docker options -d to run the application in the background.

+
+

Important Info

+All docker and docker compose specific options must be passed at the end of the script, after a -- token. +This token indicates the end of IntelOwl's options and the beginning of Docker options. + +Example: + +
./start prod up -- -d
+
+
+
+

Hint

+Starting from IntelOwl 4.0.0, with the startup script you can select which version of IntelOwl you want to run (--version). +This can be helpful to keep using old versions in case of retrocompatibility issues. The --version parameter checks out the Git Repository to the Tag of the version that you have chosen. This means that if you checkout to a v3.x.x version, you won't have the --version parameter anymore so you would need to manually checkout back to the master branch to use newer versions. +
+

Stop

+

To stop the application you have to:

+
    +
  • if executed without -d parameter: press CTRL+C
  • +
  • if executed with -d parameter: ./start prod down
  • +
+

Cleanup of database and application

+

This is a destructive operation but can be useful to start again the project from scratch

+

./start prod down -- -v

+

After Deployment

+

Users creation

+

You may want to run docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser after first run to create a superuser. +Then you can add other users directly from the Django Admin Interface after having logged with the superuser account. +To manage users, organizations and their visibility please refer to this section

+

Update and Rebuild

+

Rebuilding the project / Creating custom docker build

+

If you make some code changes and you like to rebuild the project, follow these steps:

+
    +
  1. ./start test build -- --tag=<your_tag> . to build the new docker image.
  2. +
  3. Add this new image tag in the docker/test.override.yml file.
  4. +
  5. Start the containers with ./start test up -- --build.
  6. +
+

Update to the most recent version

+

To update the project with the most recent available code you have to follow these steps:

+
$ cd <your_intel_owl_directory> # go into the project directory
+$ git pull # pull new changes
+$ ./start prod down # kill and destroy the currently running IntelOwl containers
+$ ./start prod up # restart the IntelOwl application
+
+
+

Note

+After an upgrade, sometimes a database error in Celery Containers could happen. That could be related to new DB migrations which are not applied by the main Uwsgi Container yet. Do not worry. Wait few seconds for the Uwsgi container to start correctly, then put down the application again and restart it. The problem should be solved. If not, please feel free to open an issue on Github +
+
+

Note

+After having upgraded IntelOwl, in case the application does not start and you get an error like this: + +
PermissionError: [Errno 13] Permission denied: '/var/log/intel_owl/django/authentication.log
+
+ +just run this: + +
sudo chown -R www-data:www-data /var/lib/docker/volumes/intel_owl_generic_logs/_data/django
+
+ +and restart IntelOwl. It should solve the permissions problem. + +
+
+

Warning

+Major versions of IntelOwl are usually incompatible from one another. +Maintainers strive to keep the upgrade between major version easy but it's not always like that. +Below you can find the additional process required to upgrade from each major versions. +
+

Updating to >=6.0.0 from a 5.x.x version

+

IntelOwl v6 introduced some major changes regarding how the project is started. +Before upgrading, some important things should be checked by the administrator:

+
    +
  • Docker Compose V1 support has been dropped project-wide. If you are still using a Compose version prior to v2.3.4, please upgrade to a newer version or install Docker Compose V2.
  • +
  • IntelOwl is now started with the new Bash start script that has the same options as the old Python start.py script but is more manageable and has decreased the overall project dependencies. The start.py script has now been removed. Please use the new start script instead.
  • +
  • The default message broker is now Redis. We have replaced Rabbit-MQ for Redis to allow support for Websockets in the application:
  • +
  • This change is transparent if you use our start script to run IntelOwl. That would spawn a Redis instance instead of a Rabbit-MQ one locally.
  • +
  • If you were using an external broker like AWS SQS or a managed Rabbit-MQ, they are still supported but we suggest to move to a Redis supported service to simplify the architecture (because Redis is now mandatory for Websockets)
  • +
  • Support for multiple jobs with multiple playbooks has been removed. Every Observable or File in the request will be processed by a single playbook.
  • +
  • We upgraded the base PostgreSQL image from version 12 to version 16. You have 2 choice:
  • +
  • remove your actual database and start from scratch with a new one
  • +
  • maintain your database and do not update Postgres. This could break the application at anytime because we do not support it anymore.
  • +
  • if you want to keep your old DB, follow the migration procedure you can find below
  • +
+
+

Warning

+CARE! We are providing this database migration procedure to help the users to migrate to a new PostgreSQL version. + +Upgrading PostgreSQL is outside the scope of the IntelOwl project so we do not guarantee that everything will work as intended. + +In case of doubt, please check the official PostgreSQL documentation. + +Upgrade at your own risk. + +
+

The database migration procedure is as follows:

+
    +
  • You have IntelOwl version 5.x.x up and running
  • +
  • Bring down the application (you can use the start script or manually concatenate your docker compose configuration )
  • +
  • Go inside the docker folder cd docker
  • +
  • Bring only the postgres 12 container up docker run -d --name intelowl_postgres_12 -v intel_owl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:12-alpine
  • +
  • Dump the entire database. You need the user and the database that you configured during startup for this docker exec -t intelowl_postgres_12 pg_dump -U <POSTGRES_USER> -d <POSTGRES_DB> --no-owner > /tmp/dump_intelowl.sql
  • +
  • Stop che container docker container stop intelowl_postgres_12
  • +
  • Remove the backup container docker container rm intelowl_postgres_12
  • +
  • Remove the postgres volume docker volume rm intel_owl_postgres_data <------------- remove old data, this is not exactly necessary because the new postgres has a different volume name
  • +
  • Start the intermediary postgres 16 container docker run -d --name intelowl_postgres_16 -v intelowl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:16-alpine
  • +
  • Add the data to the volume cat /tmp/dump_intelowl.sql | docker exec -i intelowl_postgres_16 psql -U <POSTGRES_USER> -d <POSTGRES_DB>
  • +
  • Stop the intermediary container docker container stop intelowl_postgres_16
  • +
  • Remove the intermediary container docker container rm intelowl_postgres_16
  • +
  • Update IntelOwl to the latest version
  • +
  • Bring up the application back again (you can use the start script or manually concatenate your docker compose configuration)
  • +
+

Updating to >=5.0.0 from a 4.x.x version

+

IntelOwl v5 introduced some major changes regarding how the plugins and their related configuration are managed in the application. +Before upgrading, some important things should be checked by the administrator:

+
    +
  • A lot of database migrations will need to be applied. Just be patient few minutes once you install the new major release. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page.
  • +
  • We moved away from the old big analyzer_config.json which was storing all the base configuration of the Analyzers to a database model (we did the same for all the other plugins types too). This allows us to manage plugins creation/modification/deletion in a more reliable manner and via the Django Admin Interface. If you have created custom plugins and changed those <plugins>_config.json file manually, you would need to re-create those custom plugins again from the Django Admin Interface. To do that please follow the related new documentation
  • +
  • We have REMOVED all the analyzers that we deprecated during the v4 releases cycle. Please substitute them with their respective new names, in case they have a replacement.
  • +
  • REMOVED Pulsedive_Active_IOC analyzer. Please substitute it with the new Pulsedive analyzer.
  • +
  • REMOVED Fortiguard analyzer because endpoint does not work anymore. No substitute.
  • +
  • REMOVED Rendertron analyzer not working as intended. No substitute.
  • +
  • REMOVED ThreatMiner, SecurityTrails and Robtex various analyzers and substituted with new versions.
  • +
  • REMOVED Doc_Info_Experimental. Its functionality (XLM Macro parsing) is moved to Doc_Info
  • +
  • REMOVED Strings_Info_Classic. Please use Strings_Info
  • +
  • REMOVED Strings_Info_ML. Please use Strings_Info and set the parameter rank_strings to True
  • +
  • REMOVED all Yara_Scan_<repo> analyzers. They all went merged in the single Yara analyzer
  • +
  • REMOVED Darksearch_Query analyzer because the service does not exist anymore. No substitute.
  • +
  • REMOVED UnpacMe_EXE_Unpacker. Please use UnpacMe
  • +
  • REMOVED BoxJS_Scan_JavaScript. Please use BoxJS
  • +
  • REMOVED all Anomali_Threatstream_<option> analyzers. Now we have a single Anomali_Threatstream analyzer. Use the parameters to select the specific API you need.
  • +
+

Updating to >=5.0.0 from a 3.x.x version

+

This is not supported. Please perform a major upgrade once at a time.

+

Updating to >=4.0.0 from a 3.x.x version

+

IntelOwl v4 introduced some major changes regarding the permission management, allowing an easier way to manage users and visibility. But that did break the previous available DB. +So, to migrate to the new major version you would need to delete your DB. To do that, you would need to delete your volumes and start the application from scratch.

+
python3 start.py prod down -v
+
+

Please be aware that, while this can be an important effort to manage, the v4 IntelOwl provides an easier way to add, invite and manage users from the application itself. See the Organization section.

+

Updating to >=2.0.0 from a 1.x.x version

+

Users upgrading from previous versions need to manually move env_file_app, env_file_postgres and env_file_integrations files under the new docker directory.

+

Updating to >v1.3.x from any prior version

+

If you are updating to >v1.3.0 from any prior version, you need to execute a helper script so that the old data present in the database doesn't break.

+
    +
  1. Follow the above updation steps, once the docker containers are up and running execute the following in a new terminal
  2. +
+
docker exec -ti intelowl_uwsgi bash
+
+

to get a shell session inside the IntelOwl's container.

+
    +
  1. Now just copy and paste the below command into this new session,
  2. +
+
curl https://gist.githubusercontent.com/Eshaan7/b111f887fa8b860c762aa38d99ec5482/raw/758517acf87f9b45bd22f06aee57251b1f3b1bbf/update_to_v1.3.0.py | python -
+
+
    +
  1. If you see "Update successful!", everything went fine and now you can enjoy the new features!
  2. +
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/introduction/index.html b/IntelOwl/introduction/index.html new file mode 100644 index 0000000..157866f --- /dev/null +++ b/IntelOwl/introduction/index.html @@ -0,0 +1,690 @@ + + + + + + + + + + + + + +Introduction - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

IntelOwl Repository

+

Introduction

+

IntelOwl was designed with the intent to help the community, in particular those researchers that can not afford commercial solutions, in the generation of threat intelligence data, in a simple, scalable and reliable way.

+

Main features:

+
    +
  • Provides enrichment of Threat Intel for malware as well as observables (IP, Domain, URL, hash, etc).
  • +
  • This application is built to scale out and to speed up the retrieval of threat info.
  • +
  • Thanks to the official libraries pyintelowl and go-intelowl, it can be integrated easily in your stack of security tools to automate common jobs usually performed, for instance, by SOC analysts manually.
  • +
  • Intel Owl is composed of:
  • +
  • analyzers that can be run to either retrieve data from external sources (like VirusTotal or AbuseIPDB) or to generate intel from internally available tools (like Yara or Oletools)
  • +
  • connectors that can be run to export data to external platforms (like MISP or OpenCTI)
  • +
  • visualizers that can be run to create custom visualizations of analyzers results
  • +
  • playbooks that are meant to make analysis easily repeatable
  • +
  • API REST written in Django and Python 3.9.
  • +
  • Built-in frontend client written in ReactJS, with certego-ui: provides features such as dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, etc.
  • +
+

Publications and media

+

To know more about the project and its growth over time, you may be interested in reading the following official blog posts and/or videos:

+ +

Feel free to ask everything it comes to your mind about the project to the author: +Matteo Lodi (Twitter).

+

We also have a dedicated twitter account for the project: @intel_owl.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/IntelOwl/static/Certego.png b/IntelOwl/static/Certego.png new file mode 100644 index 0000000..82d095b Binary files /dev/null and b/IntelOwl/static/Certego.png differ diff --git a/IntelOwl/static/abuseipdb.png b/IntelOwl/static/abuseipdb.png new file mode 100644 index 0000000..a6e002c Binary files /dev/null and b/IntelOwl/static/abuseipdb.png differ diff --git a/IntelOwl/static/accept_invite.png b/IntelOwl/static/accept_invite.png new file mode 100644 index 0000000..f82b2c6 Binary files /dev/null and b/IntelOwl/static/accept_invite.png differ diff --git a/IntelOwl/static/add_job_to_investigation.png b/IntelOwl/static/add_job_to_investigation.png new file mode 100644 index 0000000..12cf2a7 Binary files /dev/null and b/IntelOwl/static/add_job_to_investigation.png differ diff --git a/IntelOwl/static/create_investigation.png b/IntelOwl/static/create_investigation.png new file mode 100644 index 0000000..707f36b Binary files /dev/null and b/IntelOwl/static/create_investigation.png differ diff --git a/IntelOwl/static/disable_org.png b/IntelOwl/static/disable_org.png new file mode 100644 index 0000000..dfd24bb Binary files /dev/null and b/IntelOwl/static/disable_org.png differ diff --git a/IntelOwl/static/disabled.png b/IntelOwl/static/disabled.png new file mode 100644 index 0000000..b8c2aa2 Binary files /dev/null and b/IntelOwl/static/disabled.png differ diff --git a/IntelOwl/static/gsoc_logo.png b/IntelOwl/static/gsoc_logo.png new file mode 100644 index 0000000..9c5ca29 Binary files /dev/null and b/IntelOwl/static/gsoc_logo.png differ diff --git a/IntelOwl/static/honeynet_logo.png b/IntelOwl/static/honeynet_logo.png new file mode 100644 index 0000000..b6e5f96 Binary files /dev/null and b/IntelOwl/static/honeynet_logo.png differ diff --git a/IntelOwl/static/intel_owl_negative.png b/IntelOwl/static/intel_owl_negative.png new file mode 100644 index 0000000..4ae036c Binary files /dev/null and b/IntelOwl/static/intel_owl_negative.png differ diff --git a/IntelOwl/static/intel_owl_positive.png b/IntelOwl/static/intel_owl_positive.png new file mode 100644 index 0000000..58fba9d Binary files /dev/null and b/IntelOwl/static/intel_owl_positive.png differ diff --git a/IntelOwl/static/investigation_screen.png b/IntelOwl/static/investigation_screen.png new file mode 100644 index 0000000..89d0f21 Binary files /dev/null and b/IntelOwl/static/investigation_screen.png differ diff --git a/IntelOwl/static/job_options.png b/IntelOwl/static/job_options.png new file mode 100644 index 0000000..c9d9ea3 Binary files /dev/null and b/IntelOwl/static/job_options.png differ diff --git a/IntelOwl/static/notifications.png b/IntelOwl/static/notifications.png new file mode 100644 index 0000000..c926b83 Binary files /dev/null and b/IntelOwl/static/notifications.png differ diff --git a/IntelOwl/static/old_intel_owl.jpeg b/IntelOwl/static/old_intel_owl.jpeg new file mode 100644 index 0000000..1e63c1d Binary files /dev/null and b/IntelOwl/static/old_intel_owl.jpeg differ diff --git a/IntelOwl/static/old_intel_owl.png b/IntelOwl/static/old_intel_owl.png new file mode 100644 index 0000000..fb6dcbd Binary files /dev/null and b/IntelOwl/static/old_intel_owl.png differ diff --git a/IntelOwl/static/pivot_investigation.png b/IntelOwl/static/pivot_investigation.png new file mode 100644 index 0000000..9ab41a3 Binary files /dev/null and b/IntelOwl/static/pivot_investigation.png differ diff --git a/IntelOwl/static/pivot_investigation_report.png b/IntelOwl/static/pivot_investigation_report.png new file mode 100644 index 0000000..9753a5b Binary files /dev/null and b/IntelOwl/static/pivot_investigation_report.png differ diff --git a/IntelOwl/static/pivot_job_report.png b/IntelOwl/static/pivot_job_report.png new file mode 100644 index 0000000..e7409a1 Binary files /dev/null and b/IntelOwl/static/pivot_job_report.png differ diff --git a/IntelOwl/static/pivot_scan_page.png b/IntelOwl/static/pivot_scan_page.png new file mode 100644 index 0000000..dd516d9 Binary files /dev/null and b/IntelOwl/static/pivot_scan_page.png differ diff --git a/IntelOwl/static/playbook_creation.png b/IntelOwl/static/playbook_creation.png new file mode 100644 index 0000000..3d418de Binary files /dev/null and b/IntelOwl/static/playbook_creation.png differ diff --git a/IntelOwl/static/playbooks_cr.png b/IntelOwl/static/playbooks_cr.png new file mode 100644 index 0000000..75b1c06 Binary files /dev/null and b/IntelOwl/static/playbooks_cr.png differ diff --git a/IntelOwl/static/python_module_abuseipdb.png b/IntelOwl/static/python_module_abuseipdb.png new file mode 100644 index 0000000..d7e44e0 Binary files /dev/null and b/IntelOwl/static/python_module_abuseipdb.png differ diff --git a/IntelOwl/static/runtime_config.png b/IntelOwl/static/runtime_config.png new file mode 100644 index 0000000..1c43517 Binary files /dev/null and b/IntelOwl/static/runtime_config.png differ diff --git a/IntelOwl/static/runtime_config_2.png b/IntelOwl/static/runtime_config_2.png new file mode 100644 index 0000000..63b605a Binary files /dev/null and b/IntelOwl/static/runtime_config_2.png differ diff --git a/IntelOwl/static/save.png b/IntelOwl/static/save.png new file mode 100644 index 0000000..2ded433 Binary files /dev/null and b/IntelOwl/static/save.png differ diff --git a/IntelOwl/static/simple_investigation.png b/IntelOwl/static/simple_investigation.png new file mode 100644 index 0000000..368930c Binary files /dev/null and b/IntelOwl/static/simple_investigation.png differ diff --git a/IntelOwl/static/threathunter_logo.png b/IntelOwl/static/threathunter_logo.png new file mode 100644 index 0000000..32298e4 Binary files /dev/null and b/IntelOwl/static/threathunter_logo.png differ diff --git a/IntelOwl/static/visualizableBool_example.png b/IntelOwl/static/visualizableBool_example.png new file mode 100644 index 0000000..15b67cf Binary files /dev/null and b/IntelOwl/static/visualizableBool_example.png differ diff --git a/IntelOwl/static/visualizableHlist_example.png b/IntelOwl/static/visualizableHlist_example.png new file mode 100644 index 0000000..4ea6206 Binary files /dev/null and b/IntelOwl/static/visualizableHlist_example.png differ diff --git a/IntelOwl/static/visualizableLevel_example.png b/IntelOwl/static/visualizableLevel_example.png new file mode 100644 index 0000000..919479e Binary files /dev/null and b/IntelOwl/static/visualizableLevel_example.png differ diff --git a/IntelOwl/static/visualizablePage_example.png b/IntelOwl/static/visualizablePage_example.png new file mode 100644 index 0000000..2c59e88 Binary files /dev/null and b/IntelOwl/static/visualizablePage_example.png differ diff --git a/IntelOwl/static/visualizableTable_example.png b/IntelOwl/static/visualizableTable_example.png new file mode 100644 index 0000000..a139d69 Binary files /dev/null and b/IntelOwl/static/visualizableTable_example.png differ diff --git a/IntelOwl/static/visualizableTitle_example.png b/IntelOwl/static/visualizableTitle_example.png new file mode 100644 index 0000000..6e51132 Binary files /dev/null and b/IntelOwl/static/visualizableTitle_example.png differ diff --git a/IntelOwl/static/visualizableVlist_example.png b/IntelOwl/static/visualizableVlist_example.png new file mode 100644 index 0000000..fa77e65 Binary files /dev/null and b/IntelOwl/static/visualizableVlist_example.png differ diff --git a/IntelOwl/usage/index.html b/IntelOwl/usage/index.html new file mode 100644 index 0000000..b1ac6cd --- /dev/null +++ b/IntelOwl/usage/index.html @@ -0,0 +1,1864 @@ + + + + + + + + + + + + + +Usage - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+ + Skip to content + +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Usage

+

This page includes the most important things to know and understand when using IntelOwl.

+

How to interact with IntelOwl

+

Intel Owl main objective is to provide a single API interface to query in order to retrieve threat intelligence at scale.

+

There are multiple ways to interact with the Intel Owl APIs,

+
    +
  1. +

    Web Interface

    +
  2. +
  3. +

    Built-in Web interface with dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, tags management and more features

    +
  4. +
  5. +

    pyIntelOwl (CLI/SDK)

    +
  6. +
  7. +

    Official Python client that is available at: PyIntelOwl,

    +
  8. +
  9. Can be used as a library for your own python projects or...
  10. +
  11. +

    directly via the command line interface.

    +
  12. +
  13. +

    goIntelOwl (CLI/SDK)

    +
  14. +
  15. Official GO client that is available at: go-intelowl
  16. +
+
+

Hint: Tokens Creation

+The server authentication is managed by API tokens. So, if you want to interact with Intel Owl, you have two ways to do that: +
    +
  • If you are a normal user, you can go to the "API Access/Sessions" section of the GUI and create a Token there.
  • +
  • If you are an administrator of IntelOwl, you can create one or more unprivileged users from the Django Admin Interface and then generate a token for those users. +
  • +
+Afterwards you can leverage the created tokens with the Intel Owl Client. +
+

Plugins Framework

+

Plugins are the core modular components of IntelOwl that can be easily added, changed and customized. +There are several types of plugins:

+ +

Analyzers

+

Analyzers are the most important plugins in IntelOwl. They allow to perform data extraction on the observables and/or files that you would like to analyze.

+

Analyzers list

+

The following is the list of the available analyzers you can run out-of-the-box. You can also navigate the same list via the

+
    +
  • Graphical Interface: once your application is up and running, go to the "Plugins" section
  • +
  • pyintelowl: $ pyintelowl get-analyzer-config
  • +
+
File analyzers:
+
Internal tools
+ +
External services
+
    +
  • CapeSandbox: CAPESandbox automatically scans suspicious files using the CapeSandbox API. Analyzer works for private instances as well.
  • +
  • Cymru_Hash_Registry_Get_File: Check if a particular file is known to be malware by Team Cymru
  • +
  • Cuckoo_Scan: scan a file on Cuckoo (this analyzer is disabled by default. You have to change that flag in the config to use it)
  • +
  • DocGuard_Upload_File: Analyze office files in seconds. DocGuard.
  • +
  • Dragonfly_Emulation: Emulate malware against Dragonfly sandbox by Certego S.R.L.
  • +
  • FileScan_Upload_File: Upload your file to extract IoCs from executable files, documents and scripts via FileScan.io API.
  • +
  • HashLookupServer_Get_File: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
  • +
  • HybridAnalysis_Get_File: check file hash on HybridAnalysis sandbox reports
  • +
  • Intezer_Scan: scan a file on Intezer. Register for a free community account here. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • Malpedia_Scan: scan a binary or a zip file (pwd:infected) against all the yara rules available in Malpedia
  • +
  • MalwareBazaar_Get_File: Check if a particular malware sample is known to MalwareBazaar
  • +
  • MISPFIRST_Check_Hash: check a file hash on the FIRST MISP instance
  • +
  • MISP_Check_Hash: check a file hash on a MISP instance
  • +
  • MWDB_Scan: mwdblib Retrieve malware file analysis from repository maintained by CERT Polska MWDB. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • OTX_Check_Hash: check file hash on Alienvault OTX
  • +
  • SublimeSecurity: Analyze an Email with Sublime Security live flow
  • +
  • Triage_Scan: leverage Triage sandbox environment to scan various files
  • +
  • UnpacMe: UnpacMe is an automated malware unpacking service
  • +
  • Virushee_Scan: Check file hash on Virushee API. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • VirusTotal_v3_File: check the file hash on VirusTotal. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • YARAify_File_Scan: scan a file against public and non-public YARA and ClamAV signatures in YARAify public service
  • +
  • YARAify_File_Search: scan an hash against YARAify database
  • +
  • Zippy_scan : Zippy: Fast method to classify text as AI or human-generated; takes in lzma,zlib,brotli as input based engines; ensemble being default.
  • +
  • Blint: Blint is a Binary Linter that checks the security properties and capabilities of your executables. Supported binary formats: - Android (apk, aab) - ELF (GNU, musl) - PE (exe, dll) - Mach-O (x64, arm64)
  • +
+
Observable analyzers (ip, domain, url, hash)
+
Internal tools
+
    +
  • CheckDMARC: An SPF and DMARC DNS records validator for domains.
  • +
  • DNStwist: Scan a url/domain to find potentially malicious permutations via dns fuzzing. dnstwist repo
  • +
  • Thug_URL_Info: Perform hybrid dynamic/static analysis on a URL using Thug low-interaction honeyclient
  • +
+
External services
+
    +
  • AbuseIPDB: check if an ip was reported on AbuseIPDB
  • +
  • Abusix: get abuse contacts of an IP address from Abusix
  • +
  • BGP Ranking: BGP-Ranking provides a way to collect such malicious activities, aggregate the information per ASN and provide a ranking model to rank the ASN from the most malicious to the less malicious ASN.
  • +
  • Anomali_Threatstream_PassiveDNS: Return information from passive dns of Anomali. On Anomali Threatstream PassiveDNS Api.
  • +
  • Auth0: scan an IP against the Auth0 API
  • +
  • BinaryEdge: Details about an Host. List of recent events for the specified host, including details of exposed ports and services using IP query and return list of subdomains known from the target domains using domain query
  • +
  • BitcoinAbuse : Check a BTC address against bitcoinabuse.com, a public database of BTC addresses used by hackers and criminals.
  • +
  • Censys_Search: scan an IP address against Censys View API
  • +
  • CheckPhish: CheckPhish can detect phishing and fraudulent sites.
  • +
  • CIRCLPassiveDNS: scan an observable against the CIRCL Passive DNS DB
  • +
  • CIRCLPassiveSSL: scan an observable against the CIRCL Passive SSL DB
  • +
  • Classic_DNS: Retrieve current domain resolution with default DNS
  • +
  • CloudFlare_DNS: Retrieve current domain resolution with CloudFlare DoH (DNS over HTTPS)
  • +
  • CloudFlare_Malicious_Detector: Leverages CloudFlare DoH to check if a domain is related to malware
  • +
  • Crowdsec: check if an IP was reported on Crowdsec Smoke Dataset
  • +
  • Cymru_Hash_Registry_Get_Observable: Check if a particular hash is available in the malware hash registry of Team Cymru
  • +
  • DNSDB: scan an observable against the Passive DNS Farsight Database (support both v1 and v2 versions)
  • +
  • DNS0_EU: Retrieve current domain resolution with DNS0.eu DoH (DNS over HTTPS)
  • +
  • DNS0_EU_Malicious_Detector: Check if a domain or an url is marked as malicious in DNS0.eu database (Zero service)
  • +
  • DocGuard_Get: check if an hash was analyzed on DocGuard. DocGuard
  • +
  • Feodo_Tracker: Feodo Tracker offers various blocklists, helping network owners to protect their users from Dridex and Emotet/Heodo.
  • +
  • FileScan_Search: Finds reports and uploaded files by various tokens, like hash, filename, verdict, IOCs etc via FileScan.io API.
  • +
  • FireHol_IPList: check if an IP is in FireHol's IPList
  • +
  • GoogleSafebrowsing: Scan an observable against GoogleSafeBrowsing DB
  • +
  • GoogleWebRisk: Scan an observable against WebRisk API (Commercial version of Google Safe Browsing). Check the docs to enable this properly
  • +
  • Google_DNS: Retrieve current domain resolution with Google DoH (DNS over HTTPS)
  • +
  • GreedyBear: scan an IP or a domain against the GreedyBear API (requires API key)
  • +
  • GreyNoise: scan an IP against the Greynoise API (requires API key)
  • +
  • GreyNoiseCommunity: scan an IP against the Community Greynoise API (requires API key))
  • +
  • Greynoise_Labs: scan an IP against the Greynoise API (requires authentication token which can be obtained from cookies on Greynoise website after launching the playground from here)
  • +
  • HashLookupServer_Get_Observable: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
  • +
  • HoneyDB_Get: HoneyDB IP lookup service
  • +
  • HoneyDB_Scan_Twitter: scan an IP against HoneyDB.io's Twitter Threat Feed
  • +
  • Hunter_How: Scans IP and domain against Hunter_How API.
  • +
  • Hunter_Io: Scans a domain name and returns set of data about the organisation, the email address found and additional information about the people owning those email addresses.
  • +
  • HybridAnalysis_Get_Observable: search an observable in the HybridAnalysis sandbox reports
  • +
  • IP2WHOIS: API Docs IP2Location.io IP2WHOIS Domain WHOIS API helps users to obtain domain information and WHOIS record by using a domain name.
  • +
  • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
  • +
  • InQuest_DFI: Deep File Inspection by InQuest Labs
  • +
  • InQuest_IOCdb: Indicators of Compromise Database by InQuest Labs
  • +
  • InQuest_REPdb: Search in InQuest Lab's Reputation Database
  • +
  • IPApi: Get information about IPs using batch-endpoint and DNS using DNS-endpoint.
  • +
  • IPInfo: Location Information about an IP
  • +
  • Ip2location: API Docs IP2Location.io allows users to check IP address location in real time. (Supports both with or without key)
  • +
  • Intezer_Get: check if an analysis related to a hash is available in Intezer. Register for a free community account here.
  • +
  • Koodous: koodous API get information about android malware.
  • +
  • MalwareBazaar_Get_Observable: Check if a particular malware hash is known to MalwareBazaar
  • +
  • MalwareBazaar_Google_Observable: Check if a particular IP, domain or url is known to MalwareBazaar using google search
  • +
  • MaxMindGeoIP: extract GeoIP info for an observable
  • +
  • MISP: scan an observable on a MISP instance
  • +
  • MISPFIRST: scan an observable on the FIRST MISP instance
  • +
  • Mmdb_server: Mmdb_server mmdb-server is an open source fast API server to lookup IP addresses for their geographic location, AS number.
  • +
  • Mnemonic_PassiveDNS : Look up a domain or IP using the Mnemonic PassiveDNS public API.
  • +
  • MWDB_Get: mwdblib Retrieve malware file analysis by hash from repository maintained by CERT Polska MWDB.
  • +
  • Netlas: search an IP against Netlas
  • +
  • ONYPHE: search an observable in ONYPHE
  • +
  • OpenCTI: scan an observable on an OpenCTI instance
  • +
  • OTXQuery: scan an observable on Alienvault OTX
  • +
  • Phishstats: Search PhishStats API to determine if an IP/URL/domain is malicious.
  • +
  • Phishtank: Search an url against Phishtank API
  • +
  • PhishingArmy: Search an observable in the PhishingArmy blocklist
  • +
  • Pulsedive: Scan indicators and retrieve results from Pulsedive's API.
  • +
  • Quad9_DNS: Retrieve current domain resolution with Quad9 DoH (DNS over HTTPS)
  • +
  • Quad9_Malicious_Detector: Leverages Quad9 DoH to check if a domain is related to malware
  • +
  • Robtex: scan a domain/IP against the Robtex Passive DNS DB
  • +
  • Securitytrails: scan an IP/Domain against Securitytrails API
  • +
  • Shodan_Honeyscore: scan an IP against Shodan Honeyscore API
  • +
  • Shodan_Search: scan an IP against Shodan Search API
  • +
  • Spyse: Scan domains, IPs, emails and CVEs using Spyse's API. Register here.
  • +
  • SSAPINet: get a screenshot of a web page using screenshotapi.net (external source); additional config options can be added to extra_api_params in the config.
  • +
  • Stalkphish: Search Stalkphish API to retrieve information about a potential phishing site (IP/URL/domain/Generic).
  • +
  • Stratosphere_Blacklist: Cross-reference an IP from blacklists maintained by Stratosphere Labs
  • +
  • TalosReputation: check an IP reputation from Talos
  • +
  • ThreatFox: search for an IOC in ThreatFox's database
  • +
  • Threatminer: retrieve data from Threatminer API
  • +
  • TorNodesDanMeUk: check if an IP is a Tor Node using a list of all Tor nodes provided by dan.me.uk
  • +
  • TorProject: check if an IP is a Tor Exit Node
  • +
  • Triage_Search: Search for reports of observables or upload from URL on triage cloud
  • +
  • Tranco: Check if a domain is in the latest Tranco ranking top sites list
  • +
  • URLhaus: Query a domain or URL against URLhaus API.
  • +
  • UrlScan_Search: Search an IP/domain/url/hash against URLScan API
  • +
  • UrlScan_Submit_Result: Submit & retrieve result of an URL against URLScan API
  • +
  • Virushee_CheckHash: Search for a previous analysis of a file by its hash (SHA256/SHA1/MD5) on Virushee API.
  • +
  • VirusTotal_v3_Get_Observable: search an observable in the VirusTotal DB
  • +
  • Whoisxmlapi: Fetch WHOIS record data, of a domain name, an IP address, or an email address.
  • +
  • WhoIs_RipeDB_Search : Fetch whois record data of an IP address from Ripe DB using their search API (no API key required)
  • +
  • XForceExchange: scan an observable on IBM X-Force Exchange
  • +
  • YARAify_Search: lookup a file hash in Abuse.ch YARAify
  • +
  • YETI (Your Everyday Threat Intelligence): scan an observable on a YETI instance.
  • +
  • Zoomeye: Zoomeye Cyberspace Search Engine recording information of devices, websites, services and components etc..
  • +
  • Validin:Validin investigates historic and current data describing the structure and composition of the internet.
  • +
  • TweetFeed: TweetFeed collects Indicators of Compromise (IOCs) shared by the infosec community at Twitter.\r\nHere you will find malicious URLs, domains, IPs, and SHA256/MD5 hashes.
  • +
  • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
  • +
  • CyCat: CyCat or the CYbersecurity Resource CATalogue aims at mapping and documenting, in a single formalism and catalogue available cybersecurity tools, rules, playbooks, processes and controls.
  • +
  • Vulners: Vulners is the most complete and the only fully correlated security intelligence database, which goes through constant updates and links 200+ data sources in a unified machine-readable format. It contains 8 mln+ entries, including CVEs, advisories, exploits, and IoCs — everything you need to stay abreast on the latest security threats.
  • +
  • AILTypoSquatting:AILTypoSquatting is a Python library to generate list of potential typo squatting domains with domain name permutation engine to feed AIL and other systems.
  • +
+
Generic analyzers (email, phone number, etc.; anything really)
+

Some analyzers require details other than just IP, URL, Domain, etc. We classified them as generic Analyzers. Since the type of field is not known, there is a format for strings to be followed.

+
Internal tools
+
    +
  • CyberChef: Run a query on a CyberChef server using pre-defined or custom recipes.
  • +
+
External services
+
    +
  • Anomali_Threatstream_Confidence: Give max, average and minimum confidence of maliciousness for an observable. On Anomali Threatstream Confidence API.
  • +
  • Anomali_Threatstream_Intelligence: Search for threat intelligence information about an observable. On Anomali Threatstream Intelligence API.
  • +
  • CRXcavator: scans a chrome extension against crxcavator.io
  • +
  • Dehashed_Search: Query any observable/keyword against https://dehashed.com's search API.
  • +
  • EmailRep: search an email address on emailrep.io
  • +
  • HaveIBeenPwned: HaveIBeenPwned checks if an email address has been involved in a data breach
  • +
  • IntelX_Intelligent_Search: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
  • +
  • IntelX_Phonebook: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
  • +
  • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
  • +
  • MISP: scan an observable on a MISP instance
  • +
  • VirusTotal_v3_Intelligence_Search: Perform advanced queries with VirusTotal Intelligence (requires paid plan)
  • +
  • WiGLE: Maps and database of 802.11 wireless networks, with statistics, submitted by wardrivers, netstumblers, and net huggers.
  • +
  • YARAify_Generics: lookup a YARA rule (default), ClamAV rule, imphash, TLSH, telfhash or icon_dash in YARAify
  • +
  • PhoneInfoga : PhoneInfoga is one of the most advanced tools to scan international phone numbers.
  • +
  • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
  • +
+
Optional analyzers
+

Some analyzers are optional and need to be enabled explicitly.

+

Connectors

+

Connectors are designed to run after every successful analysis which makes them suitable for automated threat-sharing. They support integration with other SIEM/SOAR projects, specifically aimed at Threat Sharing Platforms.

+

Connectors list

+

The following is the list of the available connectors. You can also navigate the same list via the

+
    +
  • Graphical Interface: once your application is up and running, go to the "Plugins" section
  • +
  • pyintelowl: $ pyintelowl get-connector-config
  • +
+
List of pre-built Connectors
+
    +
  • MISP: automatically creates an event on your MISP instance, linking the successful analysis on IntelOwl.
  • +
  • OpenCTI: automatically creates an observable and a linked report on your OpenCTI instance, linking the the successful analysis on IntelOwl.
  • +
  • YETI: YETI = Your Everyday Threat Intelligence. find or create observable on YETI, linking the successful analysis on IntelOwl.
  • +
  • Slack: Send the analysis link to a Slack channel (useful for external notifications)
  • +
  • EmailSender: Send a generic email.
  • +
  • AbuseSubmitter: Send an email to request to take down a malicious domain.
  • +
+

Pivots

+

With IntelOwl v5.2.0 we introduced the Pivot Plugin.

+

Pivots are designed to create a job from another job. This plugin allows the user to set certain conditions that trigger the execution of one or more subsequent jobs, strictly connected to the first one.

+

This is a "SOAR" feature that allows the users to connect multiple analysis together.

+

List of pre-built Pivots

+
    +
  • TakedownRequestToAbuseIp: This Plugin leverages results from DNS resolver analyzers to extract a valid IP address to pivot to the Abusix analyzer.
  • +
  • AbuseIpToSubmission: This Plugin leverages results from the Abusix analyzer to extract the abuse contacts of an IP address to pivot to the AbuseSubmitter connector.
  • +
+

You can build your own custom Pivot with your custom logic with just few lines of code. See the Contribute section for more info.

+

Creating Pivots from the GUI

+

From the GUI, the users can pivot in two ways:

+
    +
  • +

    If a Job executed a Visualizer, it is possible to select a field extracted and analyze its value by clicking the "Pivot" button (see following image). In this way, the user is able to "jump" from one indicator to another. + img.png

    +
  • +
  • +

    Starting from an already existing Investigation, it is possible to select a Job block and click the "Pivot" button to analyze the same observable again, usually choosing another Playbook (see following image) + img.png

    +
  • +
+

In both cases, the user is redirected to the Scan Page that is precompiled with the observable selected. Then the user would be able to select the Playbook to execute in the new job. +img.png

+

After the new Job is started, a new Investigation will be created (if it does not already exist) and both the jobs will be added to the same Investigation.

+

In the following image you can find an example of an Investigation composed by 3 pivots generated manually:

+
    +
  • leveraging the first way to create a Pivot, the 2 Jobs that analyzed IP addresses have been generated from the first test\.com Job
  • +
  • leveraging the second way to create a Pivot, the second test\.com analysis had been created with a different Playbook.
  • +
+

img.png

+

Visualizers

+

With IntelOwl v5 we introduced a new plugin type called Visualizers. +You can leverage it as a framework to create custom aggregated and simplified visualization of analyzer results.

+

Visualizers are designed to run after the analyzers and the connectors. +The visualizer adds logic after the computations, allowing to show the final result in a different way than merely the list of reports.

+

Visualizers can be executed only during Scans through the playbook that has been configured on the visualizer itself.

+

This framework is extremely powerful and allows every user to customize the GUI as they wish. But you know...with great power comes great responsability. To fully leverage this framework, you would need to put some effort in place. You would need to understand which data is useful for you and then write few code lines that would create your own GUI. +To simplify the process, take example from the pre-built visualizers listed below and follow the dedicated documentation.

+
List of pre-built Visualizers
+
    +
  • DNS: displays the aggregation of every DNS analyzer report
  • +
  • Yara: displays the aggregation of every matched rule by the Yara Analyzer
  • +
  • Domain_Reputation: Visualizer for the Playbook "Popular_URL_Reputation_Services"
  • +
  • IP_Reputation: Visualizer for the Playbook "Popular_IP_Reputation_Services"
  • +
  • Pivot: Visualizer that can be used in a Playbook to show the Pivot execution result. See Pivots for more info.
  • +
+

Ingestors

+

With IntelOwl v5.1.0 we introduced the Ingestor Plugin.

+

Ingestors allow to automatically insert IOC streams from outside sources to IntelOwl itself. +Each Ingestor must have a Playbook attached: this will allow to create a Job from every IOC retrieved.

+

Ingestors are system-wide and disabled by default, meaning that only the administrator are able to configure them and enable them. +Ingestors can be spammy so be careful about enabling them.

+

A very powerful use is case is to combine Ingestors with Connectors to automatically extract data from external sources, analyze them with IntelOwl and push them externally to another platform (like MISP or a SIEM)

+

List of pre-built Ingestors

+
    +
  • ThreatFox: Retrieves daily ioc from https://threatfox.abuse.ch/ and analyze them.
  • +
+

Playbooks

+

Playbooks are designed to be easy to share sequence of running Plugins (Analyzers, Connectors, ...) on a particular kind of observable.

+

If you want to avoid to re-select/re-configure a particular combination of analyzers and connectors together every time, you should create a playbook out of it and use it instead. This is time saver.

+

This is a feature introduced since IntelOwl v4.1.0! Please provide feedback about it!

+

Playbooks List

+

The following is the list of the available pre-built playbooks. You can also navigate the same list via the

+
    +
  • Graphical Interface: once your application is up and running, go to the "Plugins" section
  • +
  • pyintelowl: $ pyintelowl get-playbook-config
  • +
+
List of pre-built playbooks
+
    +
  • FREE_TO_USE_ANALYZERS: A playbook containing all free to use analyzers.
  • +
  • Sample_Static_Analysis: A playbook containing all analyzers that perform static analysis on files.
  • +
  • Popular_URL_Reputation_Services: Collection of the most popular and free reputation analyzers for URLs and Domains
  • +
  • Popular_IP_Reputation_Services: Collection of the most popular and free reputation analyzers for IP addresses
  • +
  • Dns: A playbook containing all dns providers
  • +
  • Takedown_Request: Start investigation to request to take down a malicious domain. A mail will be sent to the domain's abuse contacts found
  • +
  • Abuse_IP: Playbook containing the Abusix analyzer. It is executed after the Takedown_Request playbook
  • +
  • Send_Abuse_Email: Playbook containing the AbuseSubmitter connector to send an email to request to take down a malicious domain. It is executed after the Abuse_IP playbook
  • +
+

Playbooks creation and customization

+

You can create new playbooks in different ways, based on the users you want to share them with:

+

If you want to share them to every user in IntelOwl, create them via the Django Admin interface at /admin/playbooks_manager/playbookconfig/.

+

If you want share them to yourself or your organization only, you need to leverage the "Save as Playbook" button that you can find on the top right of the Job Result Page. +In this way, after you have done an analysis, you can save the configuration of the Plugins you executed for re-use with a single click.

+

img.png

+

The created Playbook would be available to yourself only. If you want either to share it with your organization or to delete it, you need to go to the "Plugins" section and enable it manually by clicking the dedicated button.

+

img.png

+

Generic Plugin Creation, Configuration and Customization

+

If you want to create completely new Plugins (not based on already existing python modules), please refer to the Contribute section. This is usually the case when you want to integrate IntelOwl with either a new tool or a new service.

+

On the contrary, if you would like to just customize the already existing plugins, this is the place.

+

SuperUser customization

+

If you are an IntelOwl superuser, you can create, modify, delete analyzers based on already existing modules by changing the configuration values inside the Django Admin interface at:

+
    +
  • for analyzers: /admin/analyzers_manager/analyzerconfig/.
  • +
  • for connectors: /admin/connectors_manager/connectorconfig/.
  • +
  • ...and so on for all the Plugin types.
  • +
+

The following are the most important fields that you can change without touching the source code:

+
    +
  • Name: Name of the analyzer
  • +
  • Description: Description of the analyzer
  • +
  • Disabled: you can choose to disable certain analyzers, then they won't appear in the dropdown list and won't run if requested.
  • +
  • Python Module: Python path of the class that will be executed. This should not be changed most of the times.
  • +
  • Maximum TLP: see TLP Support
  • +
  • Soft Time Limit: this is the maximum time (in seconds) of execution for an analyzer. Once reached, the task will be killed (or managed in the code by a custom Exception). Default 300.
  • +
  • Routing Key: this takes effects only when multi-queue is enabled. Choose which celery worker would execute the task: local (ideal for tasks that leverage local applications like Yara), long (ideal for long tasks) or default (ideal for simple webAPI-based analyzers).
  • +
+

For analyzers only:

+
    +
  • Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a different mimetype from the ones you specified, it won't be executed
  • +
  • Not Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a mimetype from the ones you specified, it won't be executed
  • +
  • Observable Supported: can be populated as a list. If set, if you ask to analyze an observable that is not in this list, it won't be executed. Valid values are: ip, domain, url, hash, generic.
  • +
+

For connectors only:

+
    +
  • Run on Failure (default: true): if they can be run even if the job has status reported_with_fails
  • +
+

For visualizers only:

+
    +
  • Playbooks: list of playbooks that trigger the specified visualizer execution.
  • +
+

Sometimes, it may happen that you would like to create a new analyzer very similar to an already existing one. Maybe you would like to just change the description and the default parameters. +A helpful way to do that without having to copy/pasting the entire configuration, is to click on the analyzer that you want to copy, make the desired changes, and click the save as new button.

+
+

Warning

+Changing other keys can break a plugin. In that case, you should think about duplicating the configuration entry or python module with your changes. +
+

Other options can be added at the "Python module" level and not at the Plugin level. To do that, go to: admin/api_app/pythonmodule/ and select the Python module used by the Plugin that you want to change. +For example, the analyzer AbuseIPDB uses the Python module abuseipdb.AbuseIPDB.

+

img.png

+

Once there, you'll get this screen:

+

img.png

+

There you can change the following values:

+
    +
  • Update Schedule: if the analyzer require some sort of update (local database, local rules, ...), you can specify the crontab schedule to update them.
  • +
  • Health Check Schedule: if the analyzer has implemented a Health Check, you can specify the crontab schedule to check whether the service works or not.
  • +
+

Parameters

+

Each Plugin could have one or more parameters available to be configured. These parameters allow the users to customize the Plugin behavior.

+

There are 2 types of Parameters:

+
    +
  • classic Parameters
  • +
  • Secrets: these parameters usually manage sensitive data, like API keys.
  • +
+

To see the list of these parameters:

+
    +
  • You can view the "Plugin" Section in IntelOwl to have a complete and updated view of all the options available
  • +
  • You can view the parameters by exploring the Django Admin Interface:
  • +
  • admin/api_app/parameter/
  • +
  • or at the very end of each Plugin configuration like /admin/analyzers_manager/analyzerconfig/
  • +
+

You can change the Plugin Parameters at 5 different levels:

+
    +
  • if you are an IntelOwl superuser, you can go in the Django Admin Interface and change the default values of the parameters for every plugin you like. This option would change the default behavior for every user in the platform.
  • +
  • if you are either Owner or Admin of an org, you can customize the default values of the parameters for every member of the organization by leveraging the GUI in the "Organization Config" section. This overrides the previous option.
  • +
  • if you are a normal user, you can customize the default values of the parameters for your analysis only by leveraging the GUI in the "Plugin config" section. This overrides the previous option.
  • +
  • You can choose to provide runtime configuration when requesting an analysis that will override the previous options. This override is done only for the specific analysis. See Customize analyzer execution at time of request
  • +
+
+

Playbook Exception

+Please remember that, if you are executing a Playbook, the "Runtime configuration" of the Playbook take precedence over the Plugin Configuration. +
+
+

Plugin Configuration Order

+Due to the multiple chances that are given to customize the parameters of the Plugins that are executed, it may be easy to confuse the order and launch Plugins without the awereness of what you are doing. + +This is the order to define which values are used for the parameters, starting by the most important element: + +- Runtime Configuration at Time of Request. +- Runtime Configuration of the Playbook (if a Playbook is used and the Runtime Configuration at Time of Request is empty) +- Plugin Configuration of the User +- Plugin Configuration of the Organization +- Default Plugin Configuration of the Parameter + +If you are using the GUI, please remember that you can always check the Parameters before starting a "Scan" by clicking at the "Runtime configuration" ![img.png](./static/runtime_config.png) button. + +Example: +![img.png](./static/runtime_config_2.png) + +
+

Enabling or Disabling Plugins

+

By default, each available plugin is configured as either disabled or not. The majority of them are enabled by default, while others may be disabled to avoid potential problems in the application usability for first time users.

+

Considering the impact that this change could have in the application, the GUI does not allow a normal user to enable/disable any plugin. On the contrary, users with specific privileges may change this configuration:

+
    +
  • Org Administrators may leverage the feature documented here to enable/disable plugins for their org. This can be helpful to control users' behavior.
  • +
  • IntelOwl Superusers (full admin) can go to the Django Admin Interface and enable/disable them from there. This operation does overwrite the Org administrators configuration. To find the plugin to change, they'll need to first choose the section of its type ("ANALYZERS_MANAGER", "CONNECTORS_MANAGER", etc), then select the chosen plugin, change the flag on that option and save the plugin by pressing the right button.
  • +
+

img.png

+

img.png

+

Special Plugins operations

+

All plugins, i.e. analyzers and connectors, have kill and retry actions. In addition to that, all docker-based analyzers and connectors have a healthcheck action to check if their associated instances are up or not.

+
    +
  • kill:
  • +
+

Stop a plugin whose status is running/pending:

+
    +
  • GUI: Buttons on reports table on job result page.
  • +
  • PyIntelOwl: IntelOwl.kill_analyzer and IntelOwl.kill_connector function.
  • +
  • CLI: $ pyintelowl jobs kill-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs kill-connector <job_id> <connector_name>
  • +
  • +

    API: PATCH /api/job/{job_id}/{plugin_type/{plugin_name}/kill and PATCH /api/job/{job_id}/connector/{connector_name}/kill

    +
  • +
  • +

    retry:

    +
  • +
+

Retry a plugin whose status is failed/killed:

+
    +
  • GUI: Buttons on reports table on job result page.
  • +
  • PyIntelOwl: IntelOwl.retry_analyzer and IntelOwl.retry_connector function,
  • +
  • CLI: $ pyintelowl jobs retry-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs retry-connector <job_id> <connector_name>
  • +
  • +

    API: PATCH /api/job/{job_id}/{plugin_type}/{plugin_name}/retry

    +
  • +
  • +

    healthcheck:

    +
  • +
+

Check if a plugin is able to connect to its provider:

+
    +
  • GUI: Buttons on every plugin table.
  • +
  • PyIntelOwl: IntelOwl.analyzer_healthcheck and IntelOwl.connector_healthcheck methods.
  • +
  • CLI: $ pyintelowl analyzer-healthcheck <analyzer_name> and $ pyintelowl connector-healthcheck <connector_name>
  • +
  • +

    API: GET /api/{plugin_type}/{plugin_name}/healthcheck

    +
  • +
  • +

    pull:

    +
  • +
+

Update a plugin with the newest rules/database:

+
    +
  • GUI: Buttons on every plugin table.
  • +
  • API: POST /api/{plugin_type}/{plugin_name}/pull
  • +
+

TLP Support

+

The Traffic Light Protocol (TLP) is a standard that was created to facilitate greater sharing of potentially sensitive information and more effective collaboration.

+

IntelOwl is not a threat intel sharing platform, like the MISP platform. However, IntelOwl is able to share analysis results to external platforms (via Connectors) and to send possible privacy related information to external services (via Analyzers).

+

This is why IntelOwl does support a customized version of the Traffic Light Protocol (TLP): to allow the user to have a better knowledge of how their data are being shared.

+

Every Analyzer and Connector can be configured with a maximum_tlp value. +Based on that value, IntelOwl understands if the specific plugin is allowed or not to run (e.g. if maximum_tlp is GREEN, it would run for analysis with TLPs WHITE and GREEN only)

+

These is how every available TLP value behaves once selected for an analysis execution:

+
    +
  1. CLEAR: no restriction (WHITE was replaced by CLEAR in TLP v2.0, but WHITE is supported for retrocompatibility)
  2. +
  3. GREEN: disable analyzers that could impact privacy
  4. +
  5. AMBER (default): disable analyzers that could impact privacy and limit view permissions to my group
  6. +
  7. RED: disable analyzers that could impact privacy, limit view permissions to my group and do not use any external service
  8. +
+

Running a plugin

+

A plugin can be run when all of the following requirements have been satisfied:

+
    +
  1. All the required parameters of the plugin have been configured
  2. +
  3. The plugin is not disabled
  4. +
  5. The plugin is not disabled for the user's organization
  6. +
  7. If the plugin has a health check schedule, the last check has to be successful
  8. +
  9. The TLP selected to run the plugin cannot be higher than the maximum TLP configured for that plugin
  10. +
  11. The observable classification or the file mimetype has to be supported by the plugin
  12. +
+

Investigations Framework

+

Investigations are a new framework introduced in IntelOwl v6 with the goal to allow the users to connect the analysis they do with each other.

+

In this way the analysts can use IntelOwl as the starting point of their "Investigations", register their findings, correlate the information found, and collaborate...all in a single place.

+

Things to know about the framework:

+
    +
  • an Investigation is a superset of IntelOwl Jobs. It can have attached one or more existing IntelOwl Jobs
  • +
  • an Investigation contains a "description" section that can be changed and updated at anytime with new information from the analysts.
  • +
  • modification to the Investigation (description, jobs, etc) can be done by every member of the Organization where the creator of the Investigation belongs. However only they creator can delete an Investigation.
  • +
+

Create and populate an investigation

+

Investigations are created in 2 ways:

+
    +
  • automatically:
  • +
  • if you scan multiple observables at the same time, a new investigation will be created by default and all the observables they will be automatically connected to the same investigation.
  • +
  • if you run a Job with a Playbook which contains a Pivot that triggers another Job, a new investigation will be created and both the Jobs will be added to the same investigation. See how you can create a new Pivot manually from the GUI.
  • +
  • manually: by clicking on the button in the "History" section you can create an Investigation from scratch without any job attached (see following image)
  • +
+

img.png

+

If you want to add a job to an Investigation, you should click to the root block of the Investigation (see following image):

+

img_1.png

+

Once a job has been added, you'll have something like this:

+

img.png

+

If you want to remove a Job, you can click on the Job block and click "Remove branch". On the contrary, if you just want to see Job Results, you can click in the "Link" button. (check next image)

+

img.png

+

Example output of a complex investigation

+

investigation_screen.png

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/Banner.png b/Submodules/GoIntelOwl/Banner.png new file mode 100644 index 0000000..ca406d3 Binary files /dev/null and b/Submodules/GoIntelOwl/Banner.png differ diff --git a/Submodules/GoIntelOwl/LICENSE b/Submodules/GoIntelOwl/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/Submodules/GoIntelOwl/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/Submodules/GoIntelOwl/constants/constants.go b/Submodules/GoIntelOwl/constants/constants.go new file mode 100644 index 0000000..74d0a50 --- /dev/null +++ b/Submodules/GoIntelOwl/constants/constants.go @@ -0,0 +1,49 @@ +package constants + +// These represent tag endpoints URL +const ( + BASE_TAG_URL = "/api/tags" + SPECIFIC_TAG_URL = BASE_TAG_URL + "/%d" +) + +// These represent job endpoints URL +const ( + BASE_JOB_URL = "/api/jobs" + SPECIFIC_JOB_URL = BASE_JOB_URL + "/%d" + DOWNLOAD_SAMPLE_JOB_URL = SPECIFIC_JOB_URL + "/download_sample" + KILL_JOB_URL = SPECIFIC_JOB_URL + "/kill" + KILL_ANALYZER_JOB_URL = SPECIFIC_JOB_URL + "/analyzer/%s/kill" + RETRY_ANALYZER_JOB_URL = SPECIFIC_JOB_URL + "/analyzer/%s/retry" + KILL_CONNECTOR_JOB_URL = SPECIFIC_JOB_URL + "/connector/%s/kill" + RETRY_CONNECTOR_JOB_URL = SPECIFIC_JOB_URL + "/connector/%s/retry" +) + +// These represent analyzer endpoints URL +const ( + ANALYZER_CONFIG_URL = "/api/get_analyzer_configs" + ANALYZER_HEALTHCHECK_URL = "/api/analyzer/%s/healthcheck" +) + +// These represent connector endpoints URL +const ( + CONNECTOR_CONFIG_URL = "/api/get_connector_configs" + CONNECTOR_HEALTHCHECK_URL = "/api/connector/%s/healthcheck" +) + +// These represent analyze endpoints URL +const ( + ANALYZE_OBSERVABLE_URL = "/api/analyze_observable" + ANALYZE_MULTIPLE_OBSERVABLES_URL = "/api/analyze_multiple_observables" + ANALYZE_FILE_URL = "/api/analyze_file" + ANALYZE_MULTIPLE_FILES_URL = "/api/analyze_multiple_files" +) + +// These represent me endpoints URL + +const ( + BASE_ME_URL = "/api/me" + USER_DETAILS_URL = BASE_ME_URL + "/access" + ORGANIZATION_URL = BASE_ME_URL + "/organization" + INVITE_TO_ORGANIZATION_URL = ORGANIZATION_URL + "/invite" + REMOVE_MEMBER_FROM_ORGANIZATION_URL = ORGANIZATION_URL + "/remove_member" +) diff --git a/Submodules/GoIntelOwl/examples/basicExample/example.go b/Submodules/GoIntelOwl/examples/basicExample/example.go new file mode 100644 index 0000000..7b645cc --- /dev/null +++ b/Submodules/GoIntelOwl/examples/basicExample/example.go @@ -0,0 +1,65 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/intelowlproject/go-intelowl/gointelowl" + "github.com/sirupsen/logrus" +) + +func main() { + + // Configuring the IntelOwlClient! + clientOptions := gointelowl.IntelOwlClientOptions{ + Url: "PUT-YOUR-INTELOWL-INSTANCE-URL-HERE", + Token: "PUT-YOUR-TOKEN-HERE", + Certificate: "", + Timeout: 0, + } + + loggerParams := &gointelowl.LoggerParams{ + File: nil, + Formatter: &logrus.JSONFormatter{}, + Level: logrus.DebugLevel, + } + + // Making the client! + client := gointelowl.NewIntelOwlClient( + &clientOptions, + nil, + loggerParams, + ) + + ctx := context.Background() + + basicAnalysisParams := gointelowl.BasicAnalysisParams{ + User: 1, + Tlp: gointelowl.WHITE, + RuntimeConfiguration: map[string]interface{}{}, + AnalyzersRequested: []string{}, + ConnectorsRequested: []string{}, + TagsLabels: []string{}, + } + + observableAnalysisParams := gointelowl.ObservableAnalysisParams{ + BasicAnalysisParams: basicAnalysisParams, + ObservableName: "192.168.69.42", + ObservableClassification: "ip", + } + + analyzerResponse, err := client.CreateObservableAnalysis(ctx, &observableAnalysisParams) + if err != nil { + fmt.Println("err") + fmt.Println(err) + } else { + analyzerResponseJSON, _ := json.Marshal(analyzerResponse) + fmt.Println("JOB ID") + fmt.Println(analyzerResponse.JobID) + fmt.Println("JOB ID END") + fmt.Println("========== ANALYZER RESPONSE ==========") + fmt.Println(string(analyzerResponseJSON)) + fmt.Println("========== ANALYZER RESPONSE END ==========") + } +} diff --git a/Submodules/GoIntelOwl/examples/basicExample/example/index.html b/Submodules/GoIntelOwl/examples/basicExample/example/index.html new file mode 100644 index 0000000..0e7bf4c --- /dev/null +++ b/Submodules/GoIntelOwl/examples/basicExample/example/index.html @@ -0,0 +1,592 @@ + + + + + + + + + + + +Example - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Example

+

This example will show you how to do a basic scan!

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/examples/client/client.go b/Submodules/GoIntelOwl/examples/client/client.go new file mode 100644 index 0000000..2e8e5c6 --- /dev/null +++ b/Submodules/GoIntelOwl/examples/client/client.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + + "github.com/intelowlproject/go-intelowl/gointelowl" + "github.com/sirupsen/logrus" +) + +func main() { + /* + Making a new client through NewIntelOwlClient: + This takes the following parameters: + 1. IntelOwlClientOptions + 2. A *http.Client (if you do not provide one. One will be made by default) + 3. LoggerParams + These are parameters that allow you to easily configure your IntelOwlClient to your liking. + For a better understanding you can read it in the documentation: https://github.com/intelowlproject/go-intelowl/tree/main/examples/optionalParams + */ + + // Configuring the IntelOwlClient! + clientOptions := gointelowl.IntelOwlClientOptions{ + Url: "PUT-YOUR-INTELOWL-INSTANCE-URL-HERE", + Token: "PUT-YOUR-TOKEN-HERE", + Certificate: "", + Timeout: 0, + } + + // Configuring the logger + loggerParams := &gointelowl.LoggerParams{ + File: nil, + Formatter: &logrus.JSONFormatter{}, + Level: logrus.DebugLevel, + } + + // Making the client! + client := gointelowl.NewIntelOwlClient( + &clientOptions, + nil, + loggerParams, + ) + + ctx := context.Background() + + tags, err := client.TagService.List(ctx) + + if err != nil { + client.Logger.Logger.WithFields(logrus.Fields{ + "error": err.Error(), + }).Error("An error occurred") + } else { + client.Logger.Logger.WithFields(logrus.Fields{ + "tags": *tags, + }).Info("These are your tags") + } + +} diff --git a/Submodules/GoIntelOwl/examples/client/client/index.html b/Submodules/GoIntelOwl/examples/client/client/index.html new file mode 100644 index 0000000..77592c2 --- /dev/null +++ b/Submodules/GoIntelOwl/examples/client/client/index.html @@ -0,0 +1,641 @@ + + + + + + + + + + + +Client - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + + +

Client

+

A good client is a client that is easy to use, configurable and customizable to a user’s liking. Hence, the client has 4 great features: +1. Configurable HTTP client +2. Customizable timeouts +3. Logger +4. Easy ways to create the IntelOwlClient

+

Configurable HTTP client

+

Now from the documentation, you can see you can pass your http.Client. This is to facilitate each user’s requirement and taste! If you don’t pass one (nil) a default http.Client will be made for you!

+

Customizable timeouts

+

From IntelOwlClientOptions you can add your own timeout to your requests as well.

+

Logger

+

To ease developers' work go-intelowl provides a logger for easy debugging and tracking! For the logger we used logrus because of 2 reasons: +1. Easy to use +2. Extensible to your liking

+

Easy ways to create the IntelOwlClient

+

As you know working with Golang structs is sometimes cumbersome we thought we could provide a simple way to create the client in a way that helps speed up development. This gave birth to the idea of using a JSON file to create the IntelOwlClient. The method NewIntelOwlClientThroughJsonFile does exactly that. Send the IntelOwlClientOptions JSON file path with your http.Client and LoggerParams in this method and you'll get the IntelOwlClient!

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/examples/endpoints/endpoints.go b/Submodules/GoIntelOwl/examples/endpoints/endpoints.go new file mode 100644 index 0000000..fcdddb9 --- /dev/null +++ b/Submodules/GoIntelOwl/examples/endpoints/endpoints.go @@ -0,0 +1,77 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/intelowlproject/go-intelowl/gointelowl" + "github.com/sirupsen/logrus" +) + +func main() { + + // Configuring the IntelOwlClient! + clientOptions := gointelowl.IntelOwlClientOptions{ + Url: "PUT-YOUR-INTELOWL-INSTANCE-URL-HERE", + Token: "PUT-YOUR-TOKEN-HERE", + Certificate: "", + } + + loggerParams := &gointelowl.LoggerParams{ + File: nil, + Formatter: &logrus.JSONFormatter{}, + Level: logrus.DebugLevel, + } + + // Making the client! + client := gointelowl.NewIntelOwlClient( + &clientOptions, + nil, + loggerParams, + ) + + ctx := context.Background() + + /* + Now we can use the client to commnicate with your intelowl instance via the service objects! + For this example I want to Display my tags list and create a new tag! + */ + + fmt.Println("Getting the tag list!") + + // Getting the tag list! + tagList, err := client.TagService.List(ctx) + // checking for any pesky errors if there's any error it'll return an IntelOwlError + if err != nil { + fmt.Println(err) + } else { + // Iterating through the list unless its empty in that case create some using TagService.Create()! + for _, tag := range *tagList { + tagJson, err := json.Marshal(tag) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(string(tagJson)) + } + } + } + + // making the tag parameters! + tagParams := gointelowl.TagParams{ + Label: "your super duper cool tag label!", + Color: "#ffb703", + } + createdTag, err := client.TagService.Create(ctx, &tagParams) + if err != nil { + fmt.Println(err) + } else { + tagJson, err := json.Marshal(createdTag) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(string(tagJson)) + } + } + +} diff --git a/Submodules/GoIntelOwl/examples/endpoints/endpoints/index.html b/Submodules/GoIntelOwl/examples/endpoints/endpoints/index.html new file mode 100644 index 0000000..60bf1ab --- /dev/null +++ b/Submodules/GoIntelOwl/examples/endpoints/endpoints/index.html @@ -0,0 +1,588 @@ + + + + + + + + + + + +Endpoints - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/examples/optionalParams/optionalParams.go b/Submodules/GoIntelOwl/examples/optionalParams/optionalParams.go new file mode 100644 index 0000000..474b740 --- /dev/null +++ b/Submodules/GoIntelOwl/examples/optionalParams/optionalParams.go @@ -0,0 +1,56 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/intelowlproject/go-intelowl/gointelowl" + "github.com/sirupsen/logrus" +) + +/* +For this example I'll be using the tag params! +*/ +func main() { + + // Configuring the IntelOwlClient! + clientOptions := gointelowl.IntelOwlClientOptions{ + Url: "PUT-YOUR-INTELOWL-INSTANCE-URL-HERE", + Token: "PUT-YOUR-TOKEN-HERE", + Certificate: "", + } + + loggerParams := &gointelowl.LoggerParams{ + File: nil, + Formatter: &logrus.JSONFormatter{}, + Level: logrus.DebugLevel, + } + + // Making the client! + client := gointelowl.NewIntelOwlClient( + &clientOptions, + nil, + loggerParams, + ) + + ctx := context.Background() + + // making the tag parameters! + tagParams := gointelowl.TagParams{ + Label: "your super duper cool tag label!", + Color: "#ffb703", + } + createdTag, err := client.TagService.Create(ctx, &tagParams) + if err != nil { + fmt.Println(err) + } else { + tagJson, err := json.Marshal(createdTag) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(string(tagJson)) + } + } + +} diff --git a/Submodules/GoIntelOwl/examples/optionalParams/optionalParams/index.html b/Submodules/GoIntelOwl/examples/optionalParams/optionalParams/index.html new file mode 100644 index 0000000..247bc43 --- /dev/null +++ b/Submodules/GoIntelOwl/examples/optionalParams/optionalParams/index.html @@ -0,0 +1,594 @@ + + + + + + + + + + + +Optional Parameters - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Optional Parameters

+

For the sake of simplicity, we decided that for some endpoints we’ll be passing Option Parameters this is to facilitate easy access, configuration and automation so that you don’t need to pass in many parameters but just a simple struct that can be easily converted to and from JSON!

+

For example, let us look at the TagParams we use it as an argument for a method Create for TagService. From a glance, the TagParams look simple. They hold 2 fields: Label, and Color which can be passed seperatly to the method but imagine if you have many fields! (if you don’t believe see the ObservableAnalysisParams)

+

For a practical implementation you can see the example

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/go.mod b/Submodules/GoIntelOwl/go.mod new file mode 100644 index 0000000..7425c11 --- /dev/null +++ b/Submodules/GoIntelOwl/go.mod @@ -0,0 +1,10 @@ +module github.com/intelowlproject/go-intelowl + +go 1.17 + +require ( + github.com/google/go-cmp v0.5.8 + github.com/sirupsen/logrus v1.9.0 +) + +require golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect diff --git a/Submodules/GoIntelOwl/go.sum b/Submodules/GoIntelOwl/go.sum new file mode 100644 index 0000000..c63efc4 --- /dev/null +++ b/Submodules/GoIntelOwl/go.sum @@ -0,0 +1,18 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Submodules/GoIntelOwl/gointelowl/analysis.go b/Submodules/GoIntelOwl/gointelowl/analysis.go new file mode 100644 index 0000000..c729148 --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/analysis.go @@ -0,0 +1,279 @@ +package gointelowl + +import ( + "bytes" + "context" + "encoding/json" + "io" + "mime/multipart" + "os" + "path/filepath" + + "github.com/intelowlproject/go-intelowl/constants" +) + +// BasicAnalysisParams represents the common fields in an Observable and a File analysis +type BasicAnalysisParams struct { + User int `json:"user"` + Tlp TLP `json:"tlp"` + RuntimeConfiguration map[string]interface{} `json:"runtime_configuration"` + AnalyzersRequested []string `json:"analyzers_requested"` + ConnectorsRequested []string `json:"connectors_requested"` + TagsLabels []string `json:"tags_labels"` +} + +// ObservableAnalysisParams represents the fields needed to make an observable analysis. +type ObservableAnalysisParams struct { + BasicAnalysisParams + ObservableName string `json:"observable_name"` + ObservableClassification string `json:"classification"` +} + +// MultipleObservableAnalysisParams represents the fields needed to analyze multiple observables. +type MultipleObservableAnalysisParams struct { + BasicAnalysisParams + Observables [][]string `json:"observables"` +} + +// FileAnalysisParams represents the fields needed to analyze a file. +type FileAnalysisParams struct { + BasicAnalysisParams + File *os.File +} + +// MultipleFileAnalysisParams represents the fields needed to analyze multiple files. +type MultipleFileAnalysisParams struct { + BasicAnalysisParams + Files []*os.File +} + +// AnalysisResponse represents a response returned by the API when you analyze an observable or file. +type AnalysisResponse struct { + JobID int `json:"job_id"` + Status string `json:"status"` + Warnings []string `json:"warnings"` + AnalyzersRunning []string `json:"analyzers_running"` + ConnectorsRunning []string `json:"connectors_running"` +} + +// MultipleAnalysisResponse represent a response returned by the API when you analyze multiple observables or files. +type MultipleAnalysisResponse struct { + Count int `json:"count"` + Results []AnalysisResponse `json:"results"` +} + +// CreateObservableAnalysis lets you analyze an observable. +// +// Endpoint: POST /api/analyze_observable +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/analyze_observable +func (client *IntelOwlClient) CreateObservableAnalysis(ctx context.Context, params *ObservableAnalysisParams) (*AnalysisResponse, error) { + requestUrl := client.options.Url + constants.ANALYZE_OBSERVABLE_URL + method := "POST" + contentType := "application/json" + jsonData, _ := json.Marshal(params) + body := bytes.NewBuffer(jsonData) + + request, err := client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + + analysisResponse := AnalysisResponse{} + successResp, err := client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &analysisResponse); unmarshalError != nil { + return nil, unmarshalError + } + return &analysisResponse, nil + +} + +// CreateMultipleObservableAnalysis lets you analyze multiple observables. +// +// Endpoint: POST /api/analyze_multiple_observables +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/analyze_multiple_observables +func (client *IntelOwlClient) CreateMultipleObservableAnalysis(ctx context.Context, params *MultipleObservableAnalysisParams) (*MultipleAnalysisResponse, error) { + requestUrl := client.options.Url + constants.ANALYZE_MULTIPLE_OBSERVABLES_URL + method := "POST" + contentType := "application/json" + jsonData, _ := json.Marshal(params) + body := bytes.NewBuffer(jsonData) + + request, err := client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + + multipleAnalysisResponse := MultipleAnalysisResponse{} + successResp, err := client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &multipleAnalysisResponse); unmarshalError != nil { + return nil, unmarshalError + } + return &multipleAnalysisResponse, nil +} + +// CreateFileAnalysis lets you analyze a file. +// +// Endpoint: POST /api/analyze_file +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/analyze_file +func (client *IntelOwlClient) CreateFileAnalysis(ctx context.Context, fileAnalysisParams *FileAnalysisParams) (*AnalysisResponse, error) { + requestUrl := client.options.Url + constants.ANALYZE_FILE_URL + // * Making the multiform data + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + // * Adding the TLP field + writeTlpError := writer.WriteField("tlp", fileAnalysisParams.Tlp.String()) + if writeTlpError != nil { + return nil, writeTlpError + } + // * Adding the runtimeconfiguration field + runTimeConfigurationJson, marshalError := json.Marshal(fileAnalysisParams.RuntimeConfiguration) + if marshalError != nil { + return nil, marshalError + } + runTimeConfigurationJsonString := string(runTimeConfigurationJson) + writeRuntimeError := writer.WriteField("runtime_configuration", runTimeConfigurationJsonString) + if writeRuntimeError != nil { + return nil, writeRuntimeError + } + + // * Adding the requested analyzers + for _, analyzer := range fileAnalysisParams.AnalyzersRequested { + writeAnalyzerError := writer.WriteField("analyzers_requested", analyzer) + if writeAnalyzerError != nil { + return nil, writeAnalyzerError + } + } + + // * Adding the requested connectors + for _, connector := range fileAnalysisParams.ConnectorsRequested { + writeConnectorError := writer.WriteField("connectors_requested", connector) + if writeConnectorError != nil { + return nil, writeConnectorError + } + } + + // * Adding the tag labels + for _, tagLabel := range fileAnalysisParams.TagsLabels { + writeTagLabelError := writer.WriteField("tags_labels", tagLabel) + if writeTagLabelError != nil { + return nil, writeTagLabelError + } + } + + // * Adding the file! + filePart, _ := writer.CreateFormFile("file", filepath.Base(fileAnalysisParams.File.Name())) + _, writeFileError := io.Copy(filePart, fileAnalysisParams.File) + if writeFileError != nil { + writer.Close() + return nil, writeFileError + } + writer.Close() + + //* building the request! + contentType := writer.FormDataContentType() + method := "POST" + request, err := client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + analysisResponse := AnalysisResponse{} + successResp, err := client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &analysisResponse); unmarshalError != nil { + return nil, unmarshalError + } + return &analysisResponse, nil +} + +// CreateMultipleFileAnalysis lets you analyze multiple files. +// +// Endpoint: POST /api/analyze_mutliple_files +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/analyze_multiple_files +func (client *IntelOwlClient) CreateMultipleFileAnalysis(ctx context.Context, fileAnalysisParams *MultipleFileAnalysisParams) (*MultipleAnalysisResponse, error) { + requestUrl := client.options.Url + constants.ANALYZE_MULTIPLE_FILES_URL + // * Making the multiform data + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + // * Adding the TLP field + writeTlpError := writer.WriteField("tlp", fileAnalysisParams.Tlp.String()) + if writeTlpError != nil { + return nil, writeTlpError + } + // * Adding the runtimeconfiguration field + runTimeConfigurationJson, marshalError := json.Marshal(fileAnalysisParams.RuntimeConfiguration) + if marshalError != nil { + return nil, marshalError + } + runTimeConfigurationJsonString := string(runTimeConfigurationJson) + writeRuntimeError := writer.WriteField("runtime_configuration", runTimeConfigurationJsonString) + if writeRuntimeError != nil { + return nil, writeRuntimeError + } + + // * Adding the requested analyzers + for _, analyzer := range fileAnalysisParams.AnalyzersRequested { + writeAnalyzerError := writer.WriteField("analyzers_requested", analyzer) + if writeAnalyzerError != nil { + return nil, writeAnalyzerError + } + } + + // * Adding the requested connectors + for _, connector := range fileAnalysisParams.ConnectorsRequested { + writeConnectorError := writer.WriteField("connectors_requested", connector) + if writeConnectorError != nil { + return nil, writeConnectorError + } + } + + // * Adding the tag labels + for _, tagLabel := range fileAnalysisParams.TagsLabels { + writeTagLabelError := writer.WriteField("tags_labels", tagLabel) + if writeTagLabelError != nil { + return nil, writeTagLabelError + } + } + + // * Adding the files! + for _, file := range fileAnalysisParams.Files { + filePart, _ := writer.CreateFormFile("files", filepath.Base(file.Name())) + _, writeFileError := io.Copy(filePart, file) + if writeFileError != nil { + writer.Close() + return nil, writeFileError + } + } + writer.Close() + + //* building the request! + contentType := writer.FormDataContentType() + method := "POST" + request, err := client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + + multipleAnalysisResponse := MultipleAnalysisResponse{} + successResp, err := client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &multipleAnalysisResponse); unmarshalError != nil { + return nil, unmarshalError + } + return &multipleAnalysisResponse, nil +} diff --git a/Submodules/GoIntelOwl/gointelowl/analyzer.go b/Submodules/GoIntelOwl/gointelowl/analyzer.go new file mode 100644 index 0000000..510e05e --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/analyzer.go @@ -0,0 +1,96 @@ +package gointelowl + +import ( + "context" + "encoding/json" + "fmt" + "sort" + + "github.com/intelowlproject/go-intelowl/constants" +) + +// AnalyzerConfig represents how an analyzer is configured in IntelOwl. +// +// IntelOwl docs: https://intelowl.readthedocs.io/en/latest/Usage.html#analyzers-customization +type AnalyzerConfig struct { + BaseConfigurationType + Type string `json:"type"` + ExternalService bool `json:"external_service"` + LeaksInfo bool `json:"leaks_info"` + DockerBased bool `json:"docker_based"` + RunHash bool `json:"run_hash"` + RunHashType string `json:"run_hash_type"` + SupportedFiletypes []string `json:"supported_filetypes"` + NotSupportedFiletypes []string `json:"not_supported_filetypes"` + ObservableSupported []string `json:"observable_supported"` +} + +// AnalyzerService handles communication with analyzer related methods of the IntelOwl API. +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/analyzer +type AnalyzerService struct { + client *IntelOwlClient +} + +// GetConfigs lists down every analyzer configuration in your IntelOwl instance. +// +// Endpoint: GET /api/get_analyzer_configs +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/get_analyzer_configs +func (analyzerService *AnalyzerService) GetConfigs(ctx context.Context) (*[]AnalyzerConfig, error) { + requestUrl := analyzerService.client.options.Url + constants.ANALYZER_CONFIG_URL + contentType := "application/json" + method := "GET" + request, err := analyzerService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + + successResp, err := analyzerService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + analyzerConfigurationResponse := map[string]AnalyzerConfig{} + if unmarshalError := json.Unmarshal(successResp.Data, &analyzerConfigurationResponse); unmarshalError != nil { + return nil, unmarshalError + } + + analyzerNames := make([]string, 0) + // *getting all the analyzer key names! + for analyzerName := range analyzerConfigurationResponse { + analyzerNames = append(analyzerNames, analyzerName) + } + // * sorting them alphabetically + sort.Strings(analyzerNames) + analyzerConfigurationList := []AnalyzerConfig{} + for _, analyzerName := range analyzerNames { + analyzerConfig := analyzerConfigurationResponse[analyzerName] + analyzerConfigurationList = append(analyzerConfigurationList, analyzerConfig) + } + return &analyzerConfigurationList, nil +} + +// HealthCheck checks if the specified analyzer is up and running +// +// Endpoint: GET /api/analyzer/{NameOfAnalyzer}/healthcheck +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/analyzer/operation/analyzer_healthcheck_retrieve +func (analyzerService *AnalyzerService) HealthCheck(ctx context.Context, analyzerName string) (bool, error) { + route := analyzerService.client.options.Url + constants.ANALYZER_HEALTHCHECK_URL + requestUrl := fmt.Sprintf(route, analyzerName) + contentType := "application/json" + method := "GET" + request, err := analyzerService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + status := StatusResponse{} + successResp, err := analyzerService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &status); unmarshalError != nil { + return false, unmarshalError + } + return status.Status, nil +} diff --git a/Submodules/GoIntelOwl/gointelowl/client.go b/Submodules/GoIntelOwl/gointelowl/client.go new file mode 100644 index 0000000..ca03d2e --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/client.go @@ -0,0 +1,249 @@ +// go-intelowl provides an SDK to easily integrate intelowl with your own set of tools. + +// go-intelowl makes it easy to automate, configure, and use intelowl with your own set of tools +// with its Idiomatic approach making an analysis is easy as just writing one line of code! +package gointelowl + +import ( + "context" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "strings" + "time" +) + +// IntelOwlError represents an error that has occurred when communicating with IntelOwl. +type IntelOwlError struct { + StatusCode int + Message string + Response *http.Response +} + +// Error lets you implement the error interface. +// This is used for making custom go errors. +func (intelOwlError *IntelOwlError) Error() string { + errorMessage := fmt.Sprintf("Status Code: %d \n Error: %s", intelOwlError.StatusCode, intelOwlError.Message) + return errorMessage +} + +// newIntelOwlError lets you easily create new IntelOwlErrors. +func newIntelOwlError(statusCode int, message string, response *http.Response) *IntelOwlError { + return &IntelOwlError{ + StatusCode: statusCode, + Message: message, + Response: response, + } +} + +type successResponse struct { + StatusCode int + Data []byte +} + +// IntelOwlClientOptions represents the fields needed to configure and use the IntelOwlClient +type IntelOwlClientOptions struct { + Url string `json:"url"` + Token string `json:"token"` + // Certificate represents your SSL cert: path to the cert file! + Certificate string `json:"certificate"` + // Timeout is in seconds + Timeout uint64 `json:"timeout"` +} + +// IntelOwlClient handles all the communication with your IntelOwl instance. +type IntelOwlClient struct { + options *IntelOwlClientOptions + client *http.Client + TagService *TagService + JobService *JobService + AnalyzerService *AnalyzerService + ConnectorService *ConnectorService + UserService *UserService + Logger *IntelOwlLogger +} + +// TLP represents an enum for the TLP attribute used in IntelOwl's REST API. +// +// IntelOwl docs: https://intelowl.readthedocs.io/en/latest/Usage.html#tlp-support +type TLP int + +// Values of the TLP enum. +const ( + WHITE TLP = iota + 1 + GREEN + AMBER + RED +) + +// TLPVALUES represents a map to easily access the TLP values. +var TLPVALUES = map[string]int{ + "WHITE": 1, + "GREEN": 2, + "AMBER": 3, + "RED": 4, +} + +// Overriding the String method to get the string representation of the TLP enum +func (tlp TLP) String() string { + switch tlp { + case WHITE: + return "WHITE" + case GREEN: + return "GREEN" + case AMBER: + return "AMBER" + case RED: + return "RED" + } + return "WHITE" +} + +// ParseTLP is used to easily make a TLP enum +func ParseTLP(s string) TLP { + s = strings.TrimSpace(s) + value, ok := TLPVALUES[s] + if !ok { + return TLP(0) + } + return TLP(value) +} + +// Implementing the MarshalJSON interface to make our custom Marshal for the enum +func (tlp TLP) MarshalJSON() ([]byte, error) { + return json.Marshal(tlp.String()) +} + +// Implementing the UnmarshalJSON interface to make our custom Unmarshal for the enum +func (tlp *TLP) UnmarshalJSON(data []byte) (err error) { + var tlpString string + if err := json.Unmarshal(data, &tlpString); err != nil { + return err + } + if *tlp = ParseTLP(tlpString); err != nil { + return err + } + return nil +} + +// NewIntelOwlClient lets you easily create a new IntelOwlClient by providing IntelOwlClientOptions, http.Clients, and LoggerParams. +func NewIntelOwlClient(options *IntelOwlClientOptions, httpClient *http.Client, loggerParams *LoggerParams) IntelOwlClient { + + var timeout time.Duration + + if options.Timeout == 0 { + timeout = time.Duration(10) * time.Second + } else { + timeout = time.Duration(options.Timeout) * time.Second + } + + // configuring the http.Client + if httpClient == nil { + httpClient = &http.Client{ + Timeout: timeout, + } + } + + // configuring the client + client := IntelOwlClient{ + options: options, + client: httpClient, + } + + // Adding the services + client.TagService = &TagService{ + client: &client, + } + client.JobService = &JobService{ + client: &client, + } + client.AnalyzerService = &AnalyzerService{ + client: &client, + } + client.ConnectorService = &ConnectorService{ + client: &client, + } + client.UserService = &UserService{ + client: &client, + } + + // configuring the logger! + client.Logger = &IntelOwlLogger{} + client.Logger.Init(loggerParams) + + return client +} + +// NewIntelOwlClientThroughJsonFile lets you create a new IntelOwlClient through a JSON file that contains your IntelOwlClientOptions +func NewIntelOwlClientThroughJsonFile(filePath string, httpClient *http.Client, loggerParams *LoggerParams) (*IntelOwlClient, error) { + optionsBytes, err := os.ReadFile(filePath) + if err != nil { + errorMessage := fmt.Sprintf("Could not read %s", filePath) + intelOwlError := newIntelOwlError(400, errorMessage, nil) + return nil, intelOwlError + } + + intelOwlClientOptions := &IntelOwlClientOptions{} + if unmarshalError := json.Unmarshal(optionsBytes, &intelOwlClientOptions); unmarshalError != nil { + return nil, unmarshalError + } + + intelOwlClient := NewIntelOwlClient(intelOwlClientOptions, httpClient, loggerParams) + + return &intelOwlClient, nil +} + +// buildRequest is used for building requests. +func (client *IntelOwlClient) buildRequest(ctx context.Context, method string, contentType string, body io.Reader, url string) (*http.Request, error) { + request, err := http.NewRequestWithContext(ctx, method, url, body) + if err != nil { + return nil, err + } + request.Header.Set("Content-Type", contentType) + + tokenString := fmt.Sprintf("token %s", client.options.Token) + + request.Header.Set("Authorization", tokenString) + return request, nil +} + +// newRequest is used for making requests. +func (client *IntelOwlClient) newRequest(ctx context.Context, request *http.Request) (*successResponse, error) { + response, err := client.client.Do(request) + + // Checking for context errors such as reaching the deadline and/or Timeout + if err != nil { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + return nil, err + } + + defer response.Body.Close() + + msgBytes, err := ioutil.ReadAll(response.Body) + statusCode := response.StatusCode + if err != nil { + errorMessage := fmt.Sprintf("Could not convert JSON response. Status code: %d", statusCode) + intelOwlError := newIntelOwlError(statusCode, errorMessage, response) + return nil, intelOwlError + } + + if statusCode < http.StatusOK || statusCode >= http.StatusBadRequest { + errorMessage := string(msgBytes) + intelOwlError := newIntelOwlError(statusCode, errorMessage, response) + return nil, intelOwlError + } + + sucessResp := successResponse{ + StatusCode: statusCode, + Data: msgBytes, + } + + return &sucessResp, nil +} diff --git a/Submodules/GoIntelOwl/gointelowl/configuration.go b/Submodules/GoIntelOwl/gointelowl/configuration.go new file mode 100644 index 0000000..ffbf9ff --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/configuration.go @@ -0,0 +1,41 @@ +package gointelowl + +type ConfigType struct { + Queue string `json:"queue"` + SoftTimeLimit int `json:"soft_time_limit"` +} + +type Secret struct { + EnvironmentVariableKey string `json:"env_var_key"` + Description string `json:"description"` + Required bool `json:"required"` +} + +type Parameter struct { + Value interface{} `json:"value"` + Type interface{} `json:"type"` + Description string `json:"description"` +} + +type VerificationType struct { + Configured bool `json:"configured"` + ErrorMessage string `json:"error_message"` + MissingSecrets []string `json:"missing_secrets"` +} + +// BaseConfigurationType represents the common fields in an analyzer and a connector configuration. +type BaseConfigurationType struct { + Name string `json:"name"` + PythonModule string `json:"python_module"` + Disabled bool `json:"disabled"` + Description string `json:"description"` + Config ConfigType `json:"config"` + Secrets map[string]Secret `json:"secrets"` + Params map[string]Parameter `json:"params"` + Verification VerificationType `json:"verification"` +} + +// StatusResponse represents the status of an analyzer or connector i.e are they working or not. +type StatusResponse struct { + Status bool `json:"status"` +} diff --git a/Submodules/GoIntelOwl/gointelowl/connector.go b/Submodules/GoIntelOwl/gointelowl/connector.go new file mode 100644 index 0000000..ab3845f --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/connector.go @@ -0,0 +1,88 @@ +package gointelowl + +import ( + "context" + "encoding/json" + "fmt" + "sort" + + "github.com/intelowlproject/go-intelowl/constants" +) + +// ConnectorConfig represents how a connector is configured in IntelOwl. +// +// IntelOwl docs: https://intelowl.readthedocs.io/en/latest/Usage.html#connectors-customization +type ConnectorConfig struct { + BaseConfigurationType + MaximumTlp TLP `json:"maximum_tlp"` +} + +// ConnectorService handles communication with connector related methods of the IntelOwl API. +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/connector +type ConnectorService struct { + client *IntelOwlClient +} + +// GetConfigs lists down every connector configuration in your IntelOwl instance. +// +// Endpoint: GET /api/get_connector_configs +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/get_connector_configs +func (connectorService *ConnectorService) GetConfigs(ctx context.Context) (*[]ConnectorConfig, error) { + requestUrl := connectorService.client.options.Url + constants.CONNECTOR_CONFIG_URL + contentType := "application/json" + method := "GET" + request, err := connectorService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + + successResp, err := connectorService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + connectorConfigurationResponse := map[string]ConnectorConfig{} + if unmarshalError := json.Unmarshal(successResp.Data, &connectorConfigurationResponse); unmarshalError != nil { + return nil, unmarshalError + } + + connectorNames := make([]string, 0) + // *getting all the analyzer key names! + for connectorName := range connectorConfigurationResponse { + connectorNames = append(connectorNames, connectorName) + } + // * sorting them alphabetically + sort.Strings(connectorNames) + connectorConfigurationList := []ConnectorConfig{} + for _, connectorName := range connectorNames { + connectorConfig := connectorConfigurationResponse[connectorName] + connectorConfigurationList = append(connectorConfigurationList, connectorConfig) + } + return &connectorConfigurationList, nil +} + +// HealthCheck checks if the specified connector is up and running +// +// Endpoint: GET /api/connector/{NameOfConnector}/healthcheck +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/connector/operation/connector_healthcheck_retrieve +func (connectorService *ConnectorService) HealthCheck(ctx context.Context, connectorName string) (bool, error) { + route := connectorService.client.options.Url + constants.CONNECTOR_HEALTHCHECK_URL + requestUrl := fmt.Sprintf(route, connectorName) + contentType := "application/json" + method := "GET" + request, err := connectorService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + status := StatusResponse{} + successResp, err := connectorService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &status); unmarshalError != nil { + return false, unmarshalError + } + return status.Status, nil +} diff --git a/Submodules/GoIntelOwl/gointelowl/job.go b/Submodules/GoIntelOwl/gointelowl/job.go new file mode 100644 index 0000000..3fdf259 --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/job.go @@ -0,0 +1,295 @@ +package gointelowl + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/intelowlproject/go-intelowl/constants" +) + +// UserDetails represents user details in an IntelOwl job. +type UserDetails struct { + Username string `json:"username"` +} + +// Report represents a report generated by an IntelOwl job. +type Report struct { + Name string `json:"name"` + Status string `json:"status"` + Report map[string]interface{} `json:"report"` + Errors []string `json:"errors"` + ProcessTime float64 `json:"process_time"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time"` + RuntimeConfiguration map[string]interface{} `json:"runtime_configuration"` + Type string `json:"type"` +} + +// BaseJob respresents all the common fields in a Job and JobList. +type BaseJob struct { + ID int `json:"id"` + User UserDetails `json:"user"` + Tags []Tag `json:"tags"` + ProcessTime float64 `json:"process_time"` + IsSample bool `json:"is_sample"` + Md5 string `json:"md5"` + ObservableName string `json:"observable_name"` + ObservableClassification string `json:"observable_classification"` + FileName string `json:"file_name"` + FileMimetype string `json:"file_mimetype"` + Status string `json:"status"` + AnalyzersRequested []string `json:"analyzers_requested" ` + ConnectorsRequested []string `json:"connectors_requested"` + AnalyzersToExecute []string `json:"analyzers_to_execute"` + ConnectorsToExecute []string `json:"connectors_to_execute"` + ReceivedRequestTime *time.Time `json:"received_request_time"` + FinishedAnalysisTime *time.Time `json:"finished_analysis_time"` + Tlp string `json:"tlp"` + Errors []string `json:"errors"` +} + +// Job represents a job that is being processed in IntelOwl. +type Job struct { + BaseJob + AnalyzerReports []Report `json:"analyzer_reports"` + ConnectorReports []Report `json:"connector_reports"` + Permission map[string]interface{} `json:"permission"` +} + +// JobList represents a list of jobs in IntelOwl. +type JobList struct { + BaseJob +} + +type JobListResponse struct { + Count int `json:"count"` + TotalPages int `json:"total_pages"` + Results []JobList `json:"results"` +} + +// JobService handles communication with job related methods of IntelOwl API. +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs +type JobService struct { + client *IntelOwlClient +} + +// List fetches all the jobs in your IntelOwl instance. +// +// Endpoint: GET /api/jobs +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_list +func (jobService *JobService) List(ctx context.Context) (*JobListResponse, error) { + requestUrl := jobService.client.options.Url + constants.BASE_JOB_URL + contentType := "application/json" + method := "GET" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + jobList := JobListResponse{} + marashalError := json.Unmarshal(successResp.Data, &jobList) + if marashalError != nil { + return nil, marashalError + } + + return &jobList, nil +} + +// Get fetches a specific job through its job ID. +// +// Endpoint: GET /api/jobs/{jobID} +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_retrieve +func (jobService *JobService) Get(ctx context.Context, jobId uint64) (*Job, error) { + route := jobService.client.options.Url + constants.SPECIFIC_JOB_URL + requestUrl := fmt.Sprintf(route, jobId) + contentType := "application/json" + method := "GET" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + jobResponse := Job{} + unmarshalError := json.Unmarshal(successResp.Data, &jobResponse) + if unmarshalError != nil { + return nil, unmarshalError + } + return &jobResponse, nil +} + +// DownloadSample fetches the File sample with the given job through its job ID. +// +// Endpoint: GET /api/jobs/{jobID}/download_sample +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_download_sample_retrieve +func (jobService *JobService) DownloadSample(ctx context.Context, jobId uint64) ([]byte, error) { + route := jobService.client.options.Url + constants.DOWNLOAD_SAMPLE_JOB_URL + requestUrl := fmt.Sprintf(route, jobId) + contentType := "application/json" + method := "GET" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + return successResp.Data, nil +} + +// Delete removes the given job from your IntelOwl instance. +// +// Endpoint: DELETE /api/jobs/{jobID} +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_destroy +func (jobService *JobService) Delete(ctx context.Context, jobId uint64) (bool, error) { + route := jobService.client.options.Url + constants.SPECIFIC_JOB_URL + requestUrl := fmt.Sprintf(route, jobId) + contentType := "application/json" + method := "DELETE" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} + +// Kill lets you stop a running job through its ID +// +// Endpoint: PATCH /api/jobs/{jobID}/kill +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_kill_partial_update +func (jobService *JobService) Kill(ctx context.Context, jobId uint64) (bool, error) { + route := jobService.client.options.Url + constants.KILL_JOB_URL + requestUrl := fmt.Sprintf(route, jobId) + contentType := "application/json" + method := "PATCH" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} + +// KillAnalyzer lets you stop an analyzer from running on a processed job through its ID and analyzer name. +// +// Endpoint: PATCH /api/jobs/{jobID}/analyzer/{nameOfAnalyzer}/kill +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_analyzer_kill_partial_update +func (jobService *JobService) KillAnalyzer(ctx context.Context, jobId uint64, analyzerName string) (bool, error) { + route := jobService.client.options.Url + constants.KILL_ANALYZER_JOB_URL + requestUrl := fmt.Sprintf(route, jobId, analyzerName) + contentType := "application/json" + method := "PATCH" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} + +// RetryAnalyzer lets you re-run the selected analyzer on a processed job through its ID and the analyzer name. +// +// Endpoint: PATCH /api/jobs/{jobID}/analyzer/{nameOfAnalyzer}/retry +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_analyzer_retry_partial_update +func (jobService *JobService) RetryAnalyzer(ctx context.Context, jobId uint64, analyzerName string) (bool, error) { + route := jobService.client.options.Url + constants.RETRY_ANALYZER_JOB_URL + requestUrl := fmt.Sprintf(route, jobId, analyzerName) + contentType := "application/json" + method := "PATCH" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} + +// KillConnector lets you stop a connector from running on a processed job through its ID and connector name. +// +// Endpoint: PATCH /api/jobs/{jobID}/connector/{nameOfConnector}/kill +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_connector_kill_partial_update +func (jobService *JobService) KillConnector(ctx context.Context, jobId uint64, connectorName string) (bool, error) { + route := jobService.client.options.Url + constants.KILL_CONNECTOR_JOB_URL + requestUrl := fmt.Sprintf(route, jobId, connectorName) + contentType := "application/json" + method := "PATCH" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} + +// RetryConnector lets you re-run the selected connector on a processed job through its ID and connector name +// +// Endpoint: PATCH /api/jobs/{jobID}/connector/{nameOfConnector}/retry +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/jobs/operation/jobs_connector_retry_partial_update +func (jobService *JobService) RetryConnector(ctx context.Context, jobId uint64, connectorName string) (bool, error) { + route := jobService.client.options.Url + constants.RETRY_CONNECTOR_JOB_URL + requestUrl := fmt.Sprintf(route, jobId, connectorName) + contentType := "application/json" + method := "PATCH" + request, err := jobService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := jobService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} diff --git a/Submodules/GoIntelOwl/gointelowl/logger.go b/Submodules/GoIntelOwl/gointelowl/logger.go new file mode 100644 index 0000000..4d69518 --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/logger.go @@ -0,0 +1,42 @@ +package gointelowl + +import ( + "io" + "os" + + "github.com/sirupsen/logrus" +) + +// LoggerParams represents the fields to configure your logger. +type LoggerParams struct { + File io.Writer + Formatter logrus.Formatter + Level logrus.Level +} + +// IntelOwlLogger represents a logger to be used by the developer. +// IntelOwlLogger implements the Logrus logger. +// +// Logrus docs: https://github.com/sirupsen/logrus +type IntelOwlLogger struct { + Logger *logrus.Logger +} + +// Init initializes the IntelOwlLogger via LoggerParams +func (intelOwlLogger *IntelOwlLogger) Init(loggerParams *LoggerParams) { + logger := logrus.New() + + // Where to log the data! + if loggerParams.File == nil { + logger.SetOutput(os.Stdout) + } else { + logger.Out = loggerParams.File + } + + if loggerParams.Formatter != nil { + logger.SetFormatter(loggerParams.Formatter) + } + + logger.SetLevel(loggerParams.Level) + intelOwlLogger.Logger = logger +} diff --git a/Submodules/GoIntelOwl/gointelowl/me.go b/Submodules/GoIntelOwl/gointelowl/me.go new file mode 100644 index 0000000..4529c68 --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/me.go @@ -0,0 +1,215 @@ +package gointelowl + +import ( + "bytes" + "context" + "encoding/json" + "net/http" + "time" + + "github.com/intelowlproject/go-intelowl/constants" +) + +type Details struct { + Username string `json:"username"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + FullName string `json:"full_name"` + Email string `json:"email"` +} + +type AccessDetails struct { + TotalSubmissions int `json:"total_submissions"` + MonthSubmissions int `json:"month_submissions"` +} + +type User struct { + User Details `json:"user"` + Access AccessDetails `json:"access"` +} + +type UserService struct { + client *IntelOwlClient +} + +type Owner struct { + Username string `json:"username"` + FullName string `json:"full_name"` + Joined time.Time `json:"joined"` +} + +type Organization struct { + MembersCount int `json:"members_count"` + Owner Owner `json:"owner"` + IsUserOwner bool `json:"is_user_owner,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + Name string `json:"name"` +} + +type OrganizationParams struct { + Name string `json:"name"` +} + +type MemberParams struct { + Username string `json:"username"` +} + +type Invite struct { + Id int `json:"id"` + CreatedAt time.Time `json:"created_at"` + Status string `json:"status"` +} + +type Invitation struct { + Invite + Organization Organization `json:"organization"` +} + +type InvitationParams struct { + Organization OrganizationParams `json:"organization"` + Status string `json:"status"` +} + +// Access retrieves user details +// +// Endpoint: GET /api/me/access +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/me/operation/me_access_retrieve +func (userService *UserService) Access(ctx context.Context) (*User, error) { + requestUrl := userService.client.options.Url + constants.USER_DETAILS_URL + contentType := "application/json" + method := "GET" + request, err := userService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + user := User{} + successResp, err := userService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &user); unmarshalError != nil { + return nil, unmarshalError + } + return &user, nil +} + +// Organization returns the organization's details. +// +// Endpoint: GET /api/me/organization +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/me/operation/me_organization_list +func (userService *UserService) Organization(ctx context.Context) (*Organization, error) { + requestUrl := userService.client.options.Url + constants.ORGANIZATION_URL + contentType := "application/json" + method := "GET" + request, err := userService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + + org := Organization{} + successResp, err := userService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &org); unmarshalError != nil { + return nil, unmarshalError + } + return &org, nil +} + +// CreateOrganization allows you to create a super cool organization! +// +// Endpoint: POST /api/me/organization +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/me/operation/me_organization_create +func (userService *UserService) CreateOrganization(ctx context.Context, organizationParams *OrganizationParams) (*Organization, error) { + requestUrl := userService.client.options.Url + constants.ORGANIZATION_URL + // Getting the relevant JSON data + orgJson, err := json.Marshal(organizationParams) + if err != nil { + return nil, err + } + contentType := "application/json" + method := "POST" + body := bytes.NewBuffer(orgJson) + request, err := userService.client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + + org := Organization{} + successResp, err := userService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &org); unmarshalError != nil { + return nil, unmarshalError + } + return &org, nil +} + +// InviteToOrganization allows you to invite someone to your super cool organization! +// This is only accessible to the organization's owner. +// +// Endpoint: POST /api/me/organization/invite +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/me/operation/me_organization_invite_create +func (userService *UserService) InviteToOrganization(ctx context.Context, memberParams *MemberParams) (*Invite, error) { + requestUrl := userService.client.options.Url + constants.INVITE_TO_ORGANIZATION_URL + // Getting the relevant JSON data + memberJson, err := json.Marshal(memberParams) + if err != nil { + return nil, err + } + contentType := "application/json" + method := "POST" + body := bytes.NewBuffer(memberJson) + request, err := userService.client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + + invite := Invite{} + successResp, err := userService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + if unmarshalError := json.Unmarshal(successResp.Data, &invite); unmarshalError != nil { + return nil, unmarshalError + } + return &invite, nil +} + +// RemoveMemberFromOrganization lets you remove someone from your super cool organization! (you had your reasons) +// This is only accessible to the organization's owner. +// +// Endpoint: POST /api/me/organization/remove_member +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/me/operation/me_organization_create +func (userService *UserService) RemoveMemberFromOrganization(ctx context.Context, memberParams *MemberParams) (bool, error) { + requestUrl := userService.client.options.Url + constants.REMOVE_MEMBER_FROM_ORGANIZATION_URL + // Getting the relevant JSON data + memberJson, err := json.Marshal(memberParams) + if err != nil { + return false, err + } + contentType := "application/json" + method := "POST" + body := bytes.NewBuffer(memberJson) + request, err := userService.client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return false, err + } + + successResp, err := userService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} diff --git a/Submodules/GoIntelOwl/gointelowl/tag.go b/Submodules/GoIntelOwl/gointelowl/tag.go new file mode 100644 index 0000000..739a888 --- /dev/null +++ b/Submodules/GoIntelOwl/gointelowl/tag.go @@ -0,0 +1,185 @@ +package gointelowl + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/intelowlproject/go-intelowl/constants" +) + +// TagParams represents the fields needed for creating and updating tags +type TagParams struct { + Label string `json:"label"` + Color string `json:"color"` +} + +// Tag represents a tag in an IntelOwl job. +type Tag struct { + ID uint64 `json:"id"` + Label string `json:"label"` + Color string `json:"color"` +} + +// TagService handles communication with tag related methods of IntelOwl API. +// +// IntelOwl REST API tag docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/tags +type TagService struct { + client *IntelOwlClient +} + +// checkTagID is used to check if a tag ID is valid (id should be greater than zero). +func checkTagID(id uint64) error { + if id > 0 { + return nil + } + return errors.New("Tag ID cannot be 0") +} + +// List fetches all the working tags in IntelOwl. +// +// Endpoint: GET "/api/tags" +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/tags/operation/tags_list +func (tagService *TagService) List(ctx context.Context) (*[]Tag, error) { + requestUrl := tagService.client.options.Url + constants.BASE_TAG_URL + contentType := "application/json" + method := "GET" + request, err := tagService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + successResp, err := tagService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + var tagList []Tag + marashalError := json.Unmarshal(successResp.Data, &tagList) + if marashalError != nil { + return nil, marashalError + } + + return &tagList, nil +} + +// Get fetches a specific tag through its tag ID. +// +// Endpoint: GET "/api/tags/{id}" +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/tags/operation/tags_retrieve +func (tagService *TagService) Get(ctx context.Context, tagId uint64) (*Tag, error) { + if err := checkTagID(tagId); err != nil { + return nil, err + } + route := tagService.client.options.Url + constants.SPECIFIC_TAG_URL + requestUrl := fmt.Sprintf(route, tagId) + contentType := "application/json" + method := "GET" + request, err := tagService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return nil, err + } + var tagResponse Tag + successResp, err := tagService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + unmarshalError := json.Unmarshal(successResp.Data, &tagResponse) + if unmarshalError != nil { + return nil, unmarshalError + } + return &tagResponse, nil +} + +// Create lets you easily create a new tag by passing TagParams. +// +// Endpoint: POST "/api/tags/" +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/tags/operation/tags_create +func (tagService *TagService) Create(ctx context.Context, tagParams *TagParams) (*Tag, error) { + requestUrl := tagService.client.options.Url + constants.BASE_TAG_URL + tagJson, err := json.Marshal(tagParams) + if err != nil { + return nil, err + } + contentType := "application/json" + method := "POST" + body := bytes.NewBuffer(tagJson) + request, err := tagService.client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + var createdTag Tag + successResp, err := tagService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + unmarshalError := json.Unmarshal(successResp.Data, &createdTag) + if unmarshalError != nil { + return nil, unmarshalError + } + return &createdTag, nil +} + +// Update lets you edit a tag throght its tag ID. +// +// Endpoint: PUT "/api/tags/{id}" +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/tags/operation/tags_update +func (tagService *TagService) Update(ctx context.Context, tagId uint64, tagParams *TagParams) (*Tag, error) { + route := tagService.client.options.Url + constants.SPECIFIC_TAG_URL + requestUrl := fmt.Sprintf(route, tagId) + // Getting the relevant JSON data + tagJson, err := json.Marshal(tagParams) + if err != nil { + return nil, err + } + contentType := "application/json" + method := "PUT" + body := bytes.NewBuffer(tagJson) + request, err := tagService.client.buildRequest(ctx, method, contentType, body, requestUrl) + if err != nil { + return nil, err + } + var updatedTag Tag + successResp, err := tagService.client.newRequest(ctx, request) + if err != nil { + return nil, err + } + unmarshalError := json.Unmarshal(successResp.Data, &updatedTag) + if unmarshalError != nil { + return nil, unmarshalError + } + + return &updatedTag, nil +} + +// Delete removes the given tag from your IntelOwl instance. +// +// Endpoint: DELETE "/api/tags/{id}" +// +// IntelOwl REST API docs: https://intelowlproject.github.io/docs/IntelOwl/api_docs/#tag/tags/operation/tags_destroy +func (tagService *TagService) Delete(ctx context.Context, tagId uint64) (bool, error) { + if err := checkTagID(tagId); err != nil { + return false, err + } + route := tagService.client.options.Url + constants.SPECIFIC_TAG_URL + requestUrl := fmt.Sprintf(route, tagId) + contentType := "application/json" + method := "DELETE" + request, err := tagService.client.buildRequest(ctx, method, contentType, nil, requestUrl) + if err != nil { + return false, err + } + successResp, err := tagService.client.newRequest(ctx, request) + if err != nil { + return false, err + } + if successResp.StatusCode == http.StatusNoContent { + return true, nil + } + return false, nil +} diff --git a/Submodules/GoIntelOwl/index.html b/Submodules/GoIntelOwl/index.html new file mode 100644 index 0000000..28be79d --- /dev/null +++ b/Submodules/GoIntelOwl/index.html @@ -0,0 +1,722 @@ + + + + + + + + + + + +go-intelowl - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

go-intelowl

+

GitHub issues +GitHub license

+

go-banner +go-intelowl is a client library/SDK that allows developers to easily automate and integrate IntelOwl with their own set of tools!

+ +

Table of Contents

+ +

Getting Started

+

Pre requisites

+
    +
  • Go 1.17+
  • +
+

Installation

+

Use go get to retrieve the SDK to add it to your GOPATH workspace, or project's Go module dependencies.

+
$ go get github.com/intelowlproject/go-intelowl
+
+

Usage

+

This library was built with ease of use in mind! Here are some quick examples to get you started. If you need more example you can go to the examples directory

+

To start using the go-intelowl library you first need to import it: +

import "github.com/intelowlproject/go-intelowl/gointelowl"
+
+Construct a new IntelOwlClient, then use the various services to easily access different parts of Intelowl's REST API. Here's an example of getting all jobs:

+

clientOptions := gointelowl.IntelOwlClientOptions{
+    Url:         "your-cool-URL-goes-here",
+    Token:       "your-super-secret-token-goes-here",
+    // This is optional
+    Certificate: "your-optional-certificate-goes-here",
+}
+
+intelowl := gointelowl.NewIntelOwlClient(
+    &clientOptions,
+    nil
+)
+
+ctx := context.Background()
+
+// returns *[]Jobs or an IntelOwlError!
+jobs, err := intelowl.JobService.List(ctx)
+
+For easy configuration and set up we opted for options structs. Where we can customize the client API or service endpoint to our liking! For more information go here. Here's a quick example!

+
// ...Making the client and context!
+
+tagOptions = gointelowl.TagParams{
+  Label: "NEW TAG",
+  Color: "#ffb703",
+}
+
+createdTag, err := intelowl.TagService.Create(ctx, tagOptions)
+if err != nil {
+    fmt.Println(err)
+} else {
+    fmt.Println(createdTag)
+}
+
+

Examples

+

The examples directory contains a couple for clear examples, of which one is partially listed here as well:

+

package main
+
+import (
+    "fmt"
+
+    "github.com/intelowlproject/go-intelowl/gointelowl"
+)
+
+func main(){
+    intelowlOptions := gointelowl.IntelOwlClientOptions{
+        Url:         "your-cool-url-goes-here",
+        Token:       "your-super-secret-token-goes-here",
+        Certificate: "your-optional-certificate-goes-here",
+    }   
+
+    client := gointelowl.NewIntelOwlClient(
+        &intelowlOptions,
+        nil,
+    )
+
+    ctx := context.Background()
+
+    // Get User details!
+    user, err := client.UserService.Access(ctx)
+    if err != nil {
+        fmt.Println("err")
+        fmt.Println(err)
+    } else {
+        fmt.Println("USER Details")
+        fmt.Println(*user)
+    }
+}
+
+For complete usage of go-intelowl, see the full package docs.

+

Contribute

+

If you want to follow the updates, discuss, contribute, or just chat then please join our slack channel we'd love to hear your feedback!

+

License

+

Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE.

+

Links

+ +

FAQ

+

Generate API key

+

You need a valid API key to interact with the IntelOwl server.

+

v4.0 and above

+

You can get an API by doing the following: +1. Log / Signin into intelowl +2. At the upper right click on your profile from the drop down select API Access/ Sessions +3. Then generate an API key or see it!

+

v4.0 below

+

Keys should be created from the admin interface of IntelOwl: you have to go in the Durin section (click on Auth tokens) and generate a key there.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/tests/CONTRIBUTING/index.html b/Submodules/GoIntelOwl/tests/CONTRIBUTING/index.html new file mode 100644 index 0000000..3557923 --- /dev/null +++ b/Submodules/GoIntelOwl/tests/CONTRIBUTING/index.html @@ -0,0 +1,656 @@ + + + + + + + + + + + +CONTRIBUTING - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ + + + + + +

CONTRIBUTING

+

How unit tests were written

+

The unit tests were written as a combination of table driven tests and the approach used by go-github

+

Firstly we use a TestData struct that has the following fields: +1. Input - this is an interface as it is to be used as the input required for an endpoint +2. Data - this is a string as it'll be the JSON string that the endpoint is expected to return +2. StatusCode - this is an int as it is meant to be used as the expected response returned by the endpoint +3. Want - the expected struct that the method will return

+

Now the reason we made this was that these fields were needed for every endpoint hence combining them into a single struct provided us reusability and flexibility.

+

Now the testing suite used go's httptest library where we use httptest.Server as this setups a test server so that we can easily mock it. We also use http.ServerMux to mock our endpoints response.

+

How to add a new test for an endpoint

+

Lets say IntelOwl added a new endpoint called supercool in Tag. Now you've implemented the endpoint as a method of TagService and now you want to add its unit tests.

+

First go to tagService_test.go in the tests directory and add

+
func TestSuperCoolEndPoint(t *testing.T) {
+    testCases := make(map[string]TestData)
+    testCases["simple"] = TestData{
+        Input:      nil,
+        Data:       `{ "supercool": "you're a great developer :)"}`,
+        StatusCode: http.StatusOK,
+        Want: "you're a great developer :)",
+    }
+    for name, testCase := range testCases {
+        // subtest
+        t.Run(name, func(t *testing.T) {
+            // setup will give you the client, mux/router, closeServer
+            client, apiHandler, closeServer := setup()
+            defer closeServer()
+            ctx := context.Background()
+            // now you can use apiHandler to mock how the server will handle this endpoints request
+            // you can use mux/router's Handle method or HandleFunc
+            apiHandler.Handle("/api/tag/supercool", func(w http.ResponseWriter, r *http.Request) {
+                // this is a helper test to check if it is the expected request sent by the client
+                testMethod(t, r, "GET")
+                w.Write([]byte(testCase.Data))
+            })
+            expectedRespone, err := client.TagService.SuperCool(ctx)
+            if err != nil {
+                testError(t, testCase, err)
+            } else {
+                testWantData(t, testCase.Want, expectedRespone)
+            }
+        })
+    }
+}
+
+

Great! Now you've added your own unit tests.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GoIntelOwl/tests/analysisService_test.go b/Submodules/GoIntelOwl/tests/analysisService_test.go new file mode 100644 index 0000000..d7db5f1 --- /dev/null +++ b/Submodules/GoIntelOwl/tests/analysisService_test.go @@ -0,0 +1,216 @@ +package tests + +import ( + "context" + "encoding/json" + "net/http" + "os" + "path" + "testing" + + "github.com/intelowlproject/go-intelowl/constants" + "github.com/intelowlproject/go-intelowl/gointelowl" +) + +func TestCreateObservableAnalysis(t *testing.T) { + analysisJsonString := `{"job_id":260,"status":"accepted","warnings":[],"analyzers_running":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","FireHol_IPList","FileScan_Search","GoogleWebRisk","GreyNoiseCommunity","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","MalwareBazaar_Google_Observable","Mnemonic_PassiveDNS","Phishstats","Pulsedive_Active_IOC","Robtex_IP_Query","Robtex_Reverse_PDNS_Query","Stratosphere_Blacklist","TalosReputation","ThreatFox","Threatminer_PDNS","Threatminer_Reports_Tagging","TorProject","URLhaus","UrlScan_Search","WhoIs_RipeDB_Search","YETI"],"connectors_running":["YETI"]}` + analysisResponse := gointelowl.AnalysisResponse{} + if unmarshalError := json.Unmarshal([]byte(analysisJsonString), &analysisResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + basicParams := gointelowl.BasicAnalysisParams{ + User: 1, + Tlp: gointelowl.WHITE, + RuntimeConfiguration: map[string]interface{}{}, + AnalyzersRequested: []string{}, + ConnectorsRequested: []string{}, + TagsLabels: []string{}, + } + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: gointelowl.ObservableAnalysisParams{ + BasicAnalysisParams: basicParams, + ObservableName: "192.168.69.42", + ObservableClassification: "", + }, + Data: analysisJsonString, + StatusCode: http.StatusOK, + Want: &analysisResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.ANALYZE_OBSERVABLE_URL, serverHandler(t, testCase, "POST")) + observableParams, ok := testCase.Input.(gointelowl.ObservableAnalysisParams) + if ok { + gottenAnalysisResponse, err := client.CreateObservableAnalysis(ctx, &observableParams) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenAnalysisResponse) + } + } + }) + } +} + +func TestCreateMultipleObservableAnalysis(t *testing.T) { + multiAnalysisJsonString := `{"count":2,"results":[{"job_id":263,"status":"accepted","warnings":[],"analyzers_running":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","FireHol_IPList","FileScan_Search","GoogleWebRisk","GreyNoiseCommunity","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","MalwareBazaar_Google_Observable","Mnemonic_PassiveDNS","Phishstats","Pulsedive_Active_IOC","Robtex_IP_Query","Robtex_Reverse_PDNS_Query","Stratosphere_Blacklist","TalosReputation","ThreatFox","Threatminer_PDNS","Threatminer_Reports_Tagging","TorProject","URLhaus","UrlScan_Search","WhoIs_RipeDB_Search","YETI"],"connectors_running":["YETI"]},{"job_id":264,"status":"accepted","warnings":[],"analyzers_running":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","FireHol_IPList","FileScan_Search","GoogleWebRisk","GreyNoiseCommunity","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","MalwareBazaar_Google_Observable","Mnemonic_PassiveDNS","Phishstats","Pulsedive_Active_IOC","Robtex_IP_Query","Robtex_Reverse_PDNS_Query","Stratosphere_Blacklist","TalosReputation","ThreatFox","Threatminer_PDNS","Threatminer_Reports_Tagging","TorProject","URLhaus","UrlScan_Search","WhoIs_RipeDB_Search","YETI"],"connectors_running":["YETI"]}]}` + multiAnalysisResponse := gointelowl.MultipleAnalysisResponse{} + if unmarshalError := json.Unmarshal([]byte(multiAnalysisJsonString), &multiAnalysisResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + observables := make([][]string, 2) + observables[0] = make([]string, 2) + observables[0][0] = "ip" + observables[0][1] = "8.8.8.8" + observables[1] = make([]string, 2) + observables[1][0] = "ip" + observables[1][1] = "8.8.8.7" + basicAnalysisParams := gointelowl.BasicAnalysisParams{ + User: 1, + Tlp: gointelowl.WHITE, + RuntimeConfiguration: map[string]interface{}{}, + AnalyzersRequested: []string{}, + ConnectorsRequested: []string{}, + TagsLabels: []string{}, + } + + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: gointelowl.MultipleObservableAnalysisParams{ + BasicAnalysisParams: basicAnalysisParams, + Observables: observables, + }, + Data: multiAnalysisJsonString, + StatusCode: http.StatusOK, + Want: &multiAnalysisResponse, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + apiHandler.Handle(constants.ANALYZE_MULTIPLE_OBSERVABLES_URL, serverHandler(t, testCase, "POST")) + ctx := context.Background() + multipleObservableParams, ok := testCase.Input.(gointelowl.MultipleObservableAnalysisParams) + if ok { + gottenMultipleAnalysisResponse, err := client.CreateMultipleObservableAnalysis(ctx, &multipleObservableParams) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenMultipleAnalysisResponse) + } + } + }) + } + +} + +func TestCreateFileAnalysis(t *testing.T) { + analysisJsonString := `{"job_id":269,"status":"accepted","warnings":[],"analyzers_running":["File_Info"],"connectors_running":["YETI"]}` + analysisResponse := gointelowl.AnalysisResponse{} + if unmarshalError := json.Unmarshal([]byte(analysisJsonString), &analysisResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + fileName := "fileForAnalysis.txt" + fileDir := "./testFiles/" + filePath := path.Join(fileDir, fileName) + file, _ := os.Open(filePath) + defer file.Close() + basicAnalysisParams := gointelowl.BasicAnalysisParams{ + User: 1, + Tlp: gointelowl.WHITE, + RuntimeConfiguration: map[string]interface{}{}, + AnalyzersRequested: []string{"File_Info"}, + ConnectorsRequested: []string{}, + TagsLabels: []string{}, + } + fileParams := &gointelowl.FileAnalysisParams{ + BasicAnalysisParams: basicAnalysisParams, + File: file, + } + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: fileParams, + Data: analysisJsonString, + StatusCode: http.StatusOK, + Want: &analysisResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + apiHandler.Handle(constants.ANALYZE_FILE_URL, serverHandler(t, testCase, "POST")) + ctx := context.Background() + fileAnalysisParams, ok := testCase.Input.(gointelowl.FileAnalysisParams) + if ok { + gottenFileAnalysisResponse, err := client.CreateFileAnalysis(ctx, &fileAnalysisParams) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenFileAnalysisResponse) + } + } + }) + } +} + +func TestCreateMultipleFilesAnalysis(t *testing.T) { + multiAnalysisJsonString := `{"count":2,"results":[{"job_id":270,"status":"accepted","warnings":[],"analyzers_running":["File_Info"],"connectors_running":["YETI"]},{"job_id":271,"status":"accepted","warnings":[],"analyzers_running":["File_Info"],"connectors_running":["YETI"]}]}` + multiAnalysisResponse := gointelowl.MultipleAnalysisResponse{} + if unmarshalError := json.Unmarshal([]byte(multiAnalysisJsonString), &multiAnalysisResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + fileDir := "./testFiles/" + fileName := "fileForAnalysis.txt" + filePath := path.Join(fileDir, fileName) + file, _ := os.Open(filePath) + defer file.Close() + fileName2 := "fileForAnalysis.txt" + filePath2 := path.Join(fileDir, fileName2) + file2, _ := os.Open(filePath2) + defer file2.Close() + filesArray := make([]*os.File, 2) + filesArray[0] = file + filesArray[1] = file2 + basicAnalysisParams := gointelowl.BasicAnalysisParams{ + User: 1, + Tlp: gointelowl.WHITE, + RuntimeConfiguration: map[string]interface{}{}, + AnalyzersRequested: []string{"File_Info"}, + ConnectorsRequested: []string{}, + TagsLabels: []string{}, + } + multipleFileParams := &gointelowl.MultipleFileAnalysisParams{ + BasicAnalysisParams: basicAnalysisParams, + Files: filesArray, + } + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: multipleFileParams, + Data: multiAnalysisJsonString, + StatusCode: http.StatusOK, + Want: multiAnalysisResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + apiHandler.Handle(constants.ANALYZE_MULTIPLE_FILES_URL, serverHandler(t, testCase, "POST")) + ctx := context.Background() + multipleFilesAnalysisParams, ok := testCase.Input.(gointelowl.MultipleFileAnalysisParams) + if ok { + gottenMultipleFilesAnalysisResponse, err := client.CreateMultipleFileAnalysis(ctx, &multipleFilesAnalysisParams) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenMultipleFilesAnalysisResponse) + } + } + }) + } + +} diff --git a/Submodules/GoIntelOwl/tests/analyzerService_test.go b/Submodules/GoIntelOwl/tests/analyzerService_test.go new file mode 100644 index 0000000..2ce918d --- /dev/null +++ b/Submodules/GoIntelOwl/tests/analyzerService_test.go @@ -0,0 +1,137 @@ +package tests + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/intelowlproject/go-intelowl/constants" + "github.com/intelowlproject/go-intelowl/gointelowl" +) + +func TestAnalyzerServiceGetConfigs(t *testing.T) { + analyzerConfigJsonString := `{ + "APKiD_Scan_APK_DEX_JAR": { + "name": "APKiD_Scan_APK_DEX_JAR", + "python_module": "apkid.APKiD", + "disabled": false, + "description": "APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.", + "config": { + "queue": "default", + "soft_time_limit": 400 + }, + "secrets": {}, + "params": {}, + "verification": { + "configured": true, + "error_message": null, + "missing_secrets": [] + }, + "type": "file", + "external_service": false, + "leaks_info": false, + "docker_based": true, + "run_hash": false, + "supported_filetypes": [ + "application/zip", + "application/java-archive", + "application/vnd.android.package-archive", + "application/x-dex" + ], + "not_supported_filetypes": [], + "observable_supported": [] + } + }` + serverErrorString := `{"error": "Error occurred by the server"}` + badGatewayErrorString := `{"code": 502,"message": "Bad Gateway"}` + analyzerConfigurationResponse := map[string]gointelowl.AnalyzerConfig{} + if unmarshalError := json.Unmarshal([]byte(analyzerConfigJsonString), &analyzerConfigurationResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + analyzerConfigurationList := []gointelowl.AnalyzerConfig{} + for _, analyzerConfig := range analyzerConfigurationResponse { + analyzerConfigurationList = append(analyzerConfigurationList, analyzerConfig) + } + // * table test cases + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: nil, + Data: analyzerConfigJsonString, + StatusCode: http.StatusOK, + Want: analyzerConfigurationList, + } + testCases["serverError"] = TestData{ + Input: nil, + Data: serverErrorString, + StatusCode: http.StatusInternalServerError, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusInternalServerError, + Message: serverErrorString, + }, + } + testCases["badGateway"] = TestData{ + Input: nil, + Data: badGatewayErrorString, + StatusCode: http.StatusBadGateway, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadGateway, + Message: badGatewayErrorString, + }, + } + for name, testCase := range testCases { + // *Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.ANALYZER_CONFIG_URL, serverHandler(t, testCase, "GET")) + gottenAnalyzerConfigList, err := client.AnalyzerService.GetConfigs(ctx) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, (*gottenAnalyzerConfigList)) + } + }) + } +} + +func TestAnalyzerServiceHealthCheck(t *testing.T) { + // * table test cases + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: "Floss", + Data: `{"status": true}`, + StatusCode: http.StatusOK, + Want: true, + } + testCases["analyzerDoesntExist"] = TestData{ + Input: "notAnAnalyzer", + Data: `{"errors": {"detail": "Analyzer doesn't exist"}}`, + StatusCode: http.StatusBadRequest, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadRequest, + Message: `{"errors": {"detail": "Analyzer doesn't exist"}}`, + }, + } + for name, testCase := range testCases { + // *Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + input, ok := testCase.Input.(string) + if ok { + testUrl := fmt.Sprintf(constants.ANALYZER_HEALTHCHECK_URL, input) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET")) + status, err := client.AnalyzerService.HealthCheck(ctx, input) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, status) + } + } + }) + } +} diff --git a/Submodules/GoIntelOwl/tests/connectorService_test.go b/Submodules/GoIntelOwl/tests/connectorService_test.go new file mode 100644 index 0000000..e6cef8b --- /dev/null +++ b/Submodules/GoIntelOwl/tests/connectorService_test.go @@ -0,0 +1,95 @@ +package tests + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "sort" + "testing" + + "github.com/intelowlproject/go-intelowl/constants" + "github.com/intelowlproject/go-intelowl/gointelowl" +) + +func TestConnectorServiceGetConfigs(t *testing.T) { + connectorConfigJsonString := `{"MISP":{"name":"MISP","python_module":"misp.MISP","disabled":true,"description":"Automatically creates an event on your MISP instance, linking the successful analysis on IntelOwl","config":{"queue":"default","soft_time_limit":30},"secrets":{"api_key_name":{"env_var_key":"CONNECTOR_MISP_KEY","description":"API key for your MISP instance","required":true},"url_key_name":{"env_var_key":"CONNECTOR_MISP_URL","description":"URL of your MISP instance","required":true}},"params":{"ssl_check":{"value":true,"type":"bool","description":"Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled."},"debug":{"value":false,"type":"bool","description":"Enable debug logs."},"tlp":{"value":"white","type":"str","description":"Change this as per your organization's threat sharing conventions."}},"verification":{"configured":false,"error_message":"(api_key_name,url_key_name) not set; (0 of 2 satisfied)","missing_secrets":["api_key_name","url_key_name"]},"maximum_tlp":"WHITE"},"OpenCTI":{"name":"OpenCTI","python_module":"opencti.OpenCTI","disabled":true,"description":"Automatically creates an observable and a linked report on your OpenCTI instance, linking the successful analysis on IntelOwl","config":{"queue":"default","soft_time_limit":30},"secrets":{"api_key_name":{"env_var_key":"CONNECTOR_OPENCTI_KEY","description":"API key for your OpenCTI instance","required":true},"url_key_name":{"env_var_key":"CONNECTOR_OPENCTI_URL","description":"URL of your OpenCTI instance","required":true}},"params":{"ssl_verify":{"value":true,"type":"bool","description":"Enable SSL certificate server verification. Change this if your OpenCTI instance has not SSL enabled."},"proxies":{"value":{"http":"","https":""},"type":"dict","description":"Use these options to pass your request through a proxy server."},"tlp":{"value":{"type":"white","color":"#ffffff","x_opencti_order":1},"type":"dict","description":"Change this as per your organization's threat sharing conventions."}},"verification":{"configured":false,"error_message":"(api_key_name,url_key_name) not set; (0 of 2 satisfied)","missing_secrets":["api_key_name","url_key_name"]},"maximum_tlp":"WHITE"},"YETI":{"name":"YETI","python_module":"yeti.YETI","disabled":true,"description":"find or create observable on YETI, linking the successful analysis on IntelOwl.","config":{"queue":"default","soft_time_limit":30},"secrets":{"api_key_name":{"env_var_key":"CONNECTOR_YETI_KEY","description":"API key for your YETI instance","required":true},"url_key_name":{"env_var_key":"CONNECTOR_YETI_URL","description":"API URL of your YETI instance","required":true}},"params":{"verify_ssl":{"value":true,"type":"bool","description":"Enable SSL certificate server verification. Change this if your YETI instance has not SSL enabled."}},"verification":{"configured":false,"error_message":"(api_key_name,url_key_name) not set; (0 of 2 satisfied)","missing_secrets":["api_key_name","url_key_name"]},"maximum_tlp":"WHITE"}}` + connectorConfigurationResponse := map[string]gointelowl.ConnectorConfig{} + if unmarshalError := json.Unmarshal([]byte(connectorConfigJsonString), &connectorConfigurationResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + connectorNames := make([]string, 0) + // *getting all the analyzer key names! + for connectorName := range connectorConfigurationResponse { + connectorNames = append(connectorNames, connectorName) + } + // * sorting them alphabetically + sort.Strings(connectorNames) + connectorConfigurationList := []gointelowl.ConnectorConfig{} + for _, connectorName := range connectorNames { + connectorConfig := connectorConfigurationResponse[connectorName] + connectorConfigurationList = append(connectorConfigurationList, connectorConfig) + } + // * table test cases + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: nil, + Data: connectorConfigJsonString, + StatusCode: http.StatusOK, + Want: connectorConfigurationList, + } + for name, testCase := range testCases { + // *Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.CONNECTOR_CONFIG_URL, serverHandler(t, testCase, "GET")) + gottenConnectorConfigList, err := client.ConnectorService.GetConfigs(ctx) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, *gottenConnectorConfigList) + } + }) + } +} + +func TestConnectorServiceHealthCheck(t *testing.T) { + // * table test cases + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: "OpenCTI", + Data: `{"status": false}`, + StatusCode: http.StatusOK, + Want: false, + } + testCases["connectorDoesntExist"] = TestData{ + Input: "notAConnector", + Data: `{"errors": {"detail": "Connector doesn't exist"}}`, + StatusCode: http.StatusBadRequest, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadRequest, + Message: `{"errors": {"detail": "Connector doesn't exist"}}`, + }, + } + for name, testCase := range testCases { + // *Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + input, ok := testCase.Input.(string) + if ok { + testUrl := fmt.Sprintf(constants.CONNECTOR_HEALTHCHECK_URL, input) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET")) + status, err := client.ConnectorService.HealthCheck(ctx, input) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, status) + } + } + }) + } +} diff --git a/Submodules/GoIntelOwl/tests/intelOwl_test.go b/Submodules/GoIntelOwl/tests/intelOwl_test.go new file mode 100644 index 0000000..c9e0d16 --- /dev/null +++ b/Submodules/GoIntelOwl/tests/intelOwl_test.go @@ -0,0 +1,99 @@ +package tests + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/intelowlproject/go-intelowl/gointelowl" + "github.com/sirupsen/logrus" +) + +// * Test Data Struct used for every struct obj +type TestData struct { + Input interface{} + Data string + StatusCode int + Want interface{} +} + +// Setting up the router, client, and test server +func setup() (testClient gointelowl.IntelOwlClient, apiHandler *http.ServeMux, closeServer func()) { + + apiHandler = http.NewServeMux() + + testServer := httptest.NewServer(apiHandler) + + testClient = NewTestIntelOwlClient(testServer.URL) + + return testClient, apiHandler, testServer.Close + +} + +// Helper test +// Testing the request method is as expected +func testMethod(t *testing.T, request *http.Request, wantedMethod string) { + t.Helper() + if got := request.Method; got != wantedMethod { + t.Errorf("Request method: %v, want %v", got, wantedMethod) + } +} + +// Helper test +// Testing if it was an Error response +func testError(t *testing.T, testData TestData, err error) { + t.Helper() + if testData.StatusCode < http.StatusOK || testData.StatusCode >= http.StatusBadRequest { + diff := cmp.Diff(testData.Want, err, cmpopts.IgnoreFields(gointelowl.IntelOwlError{}, "Response")) + if diff != "" { + t.Fatalf(diff) + } + } +} + +// Helper test +// Testing if it was the expected response +func testWantData(t *testing.T, want interface{}, data interface{}) { + t.Helper() + diff := cmp.Diff(want, data) + if diff != "" { + t.Fatalf(diff) + } +} + +func serverHandler(t *testing.T, testData TestData, expectedMethod string) http.Handler { + handler := func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, expectedMethod) + if testData.StatusCode > 0 { + w.WriteHeader(testData.StatusCode) + } + if len(testData.Data) > 0 { + _, err := w.Write([]byte(testData.Data)) + if err != nil { + //* writing an empty object to signifiy could not convert data! + fmt.Fprintf(w, "{}") + } + } + } + + return http.HandlerFunc(handler) +} + +func NewTestIntelOwlClient(url string) gointelowl.IntelOwlClient { + return gointelowl.NewIntelOwlClient( + &gointelowl.IntelOwlClientOptions{ + Url: url, + Token: "test-token", + Certificate: "", + }, + nil, + &gointelowl.LoggerParams{ + File: nil, + Formatter: nil, + Level: logrus.DebugLevel, + }, + ) +} diff --git a/Submodules/GoIntelOwl/tests/jobService_test.go b/Submodules/GoIntelOwl/tests/jobService_test.go new file mode 100644 index 0000000..9dfcda3 --- /dev/null +++ b/Submodules/GoIntelOwl/tests/jobService_test.go @@ -0,0 +1,389 @@ +package tests + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/intelowlproject/go-intelowl/constants" + "github.com/intelowlproject/go-intelowl/gointelowl" +) + +func TestJobServiceList(t *testing.T) { + jobListJson := `{"count":8,"total_pages":1,"results":[{"id":74,"user":{"username":"hussain"},"tags":[],"process_time":102.05,"is_sample":false,"md5":"91c0c59c8f6fc9aa2dc99a89f2fd0ab5","observable_name":"string2","observable_classification":"generic","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["CryptoScamDB_CheckAPI","CRXcavator","CyberChef","Darksearch_Query","FileScan_Search","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","Phishstats","ThreatFox","YARAify_Generics","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T20:54:48.734361Z","finished_analysis_time":"2022-07-15T20:56:30.779578Z","tlp":"WHITE","errors":[]},{"id":73,"user":{"username":"hussain"},"tags":[],"process_time":73.68,"is_sample":false,"md5":"54099216c16afededa934713c7396aa7","observable_name":"sknknksndksnd","observable_classification":"generic","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["CryptoScamDB_CheckAPI","CRXcavator","CyberChef","Darksearch_Query","FileScan_Search","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","Phishstats","ThreatFox","YARAify_Generics","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T20:27:48.244790Z","finished_analysis_time":"2022-07-15T20:29:01.924146Z","tlp":"WHITE","errors":[]},{"id":72,"user":{"username":"hussain"},"tags":[],"process_time":87.87,"is_sample":false,"md5":"40ff44d9e619b17524bf3763204f9cbb","observable_name":"8.8.8.8","observable_classification":"ip","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","GoogleWebRisk","FileScan_Search","InQuest_IOCdb","GreyNoiseAlpha","GreyNoiseCommunity"],"connectors_requested":[],"analyzers_to_execute":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","GoogleWebRisk","FileScan_Search","InQuest_IOCdb","GreyNoiseCommunity"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T20:25:44.041286Z","finished_analysis_time":"2022-07-15T20:27:11.909898Z","tlp":"WHITE","errors":[]},{"id":71,"user":{"username":"hussain"},"tags":[],"process_time":null,"is_sample":false,"md5":"8fa14cdd754f91cc6554c9e71929cce7","observable_name":"f","observable_classification":"generic","file_name":"","file_mimetype":"","status":"killed","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["CryptoScamDB_CheckAPI","CRXcavator","CyberChef","Darksearch_Query","FileScan_Search","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","Phishstats","ThreatFox","YARAify_Generics","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T19:29:59.832445Z","finished_analysis_time":null,"tlp":"WHITE","errors":[]},{"id":70,"user":{"username":"hussain"},"tags":[],"process_time":32.67,"is_sample":false,"md5":"99754106633f94d350db34d548d6091a","observable_name":"fuck","observable_classification":"generic","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["CryptoScamDB_CheckAPI","CRXcavator","CyberChef","Darksearch_Query","FileScan_Search","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","Phishstats","ThreatFox","YARAify_Generics","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T19:29:10.045774Z","finished_analysis_time":"2022-07-15T19:29:42.713338Z","tlp":"WHITE","errors":[]},{"id":69,"user":{"username":"hussain"},"tags":[],"process_time":39.69,"is_sample":false,"md5":"40ff44d9e619b17524bf3763204f9cbb","observable_name":"8.8.8.8","observable_classification":"ip","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","FireHol_IPList","FileScan_Search","GoogleWebRisk","GreyNoiseCommunity","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","MalwareBazaar_Google_Observable","Mnemonic_PassiveDNS","Phishstats","Pulsedive_Active_IOC","Robtex_IP_Query","Robtex_Reverse_PDNS_Query","Stratosphere_Blacklist","TalosReputation","ThreatFox","Threatminer_PDNS","Threatminer_Reports_Tagging","TorProject","URLhaus","UrlScan_Search","WhoIs_RipeDB_Search","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T19:26:47.638794Z","finished_analysis_time":"2022-07-15T19:27:27.325066Z","tlp":"WHITE","errors":[]},{"id":68,"user":{"username":"hussain"},"tags":[],"process_time":null,"is_sample":false,"md5":"40ff44d9e619b17524bf3763204f9cbb","observable_name":"8.8.8.8","observable_classification":"ip","file_name":"","file_mimetype":"","status":"killed","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","FireHol_IPList","FileScan_Search","GoogleWebRisk","GreyNoiseCommunity","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","MalwareBazaar_Google_Observable","Mnemonic_PassiveDNS","Phishstats","Pulsedive_Active_IOC","Robtex_IP_Query","Robtex_Reverse_PDNS_Query","Stratosphere_Blacklist","TalosReputation","ThreatFox","Threatminer_PDNS","Threatminer_Reports_Tagging","TorProject","URLhaus","UrlScan_Search","WhoIs_RipeDB_Search","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T18:51:02.901898Z","finished_analysis_time":null,"tlp":"WHITE","errors":[]},{"id":67,"user":{"username":"hussain"},"tags":[],"process_time":82.88,"is_sample":false,"md5":"70047c83e6cfab6f85cf9fdf0cb4fdff","observable_name":"ransomware","observable_classification":"generic","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":[],"connectors_requested":[],"analyzers_to_execute":["CryptoScamDB_CheckAPI","CRXcavator","CyberChef","Darksearch_Query","FileScan_Search","InQuest_IOCdb","InQuest_REPdb","InQuest_DFI","Phishstats","ThreatFox","YARAify_Generics","YETI"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T18:33:10.719610Z","finished_analysis_time":"2022-07-15T18:34:33.601332Z","tlp":"WHITE","errors":[]}]}` + jobList := gointelowl.JobListResponse{} + err := json.Unmarshal([]byte(jobListJson), &jobList) + if err != nil { + t.Fatalf("Unexpected error - could not parse job list json") + } else { + // * table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: nil, + Data: jobListJson, + StatusCode: http.StatusOK, + Want: &jobList, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.BASE_JOB_URL, serverHandler(t, testCase, "GET")) + gottenJobList, err := client.JobService.List(ctx) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenJobList) + } + }) + } + } +} + +func TestJobServiceGet(t *testing.T) { + jobJsonString := `{"id":72,"user":{"username":"hussain"},"tags":[],"process_time":87.87,"analyzer_reports":[{"name":"CryptoScamDB_CheckAPI","status":"SUCCESS","report":{"input":"8.8.8.8","result":{"type":"ip","status":"neutral","entries":[]},"success":true},"errors":[],"process_time":1.91,"start_time":"2022-07-15T20:25:45.681509Z","end_time":"2022-07-15T20:25:47.595517Z","runtime_configuration":{},"type":"analyzer"},{"name":"Darksearch_Query","status":"FAILED","report":{},"errors":["DarkSearchRequestException: "],"process_time":1.51,"start_time":"2022-07-15T20:25:45.681505Z","end_time":"2022-07-15T20:25:47.189974Z","runtime_configuration":{},"type":"analyzer"},{"name":"Classic_DNS","status":"SUCCESS","report":{"observable":"8.8.8.8","resolutions":["dns.google"]},"errors":[],"process_time":0.51,"start_time":"2022-07-15T20:25:47.601891Z","end_time":"2022-07-15T20:25:48.116356Z","runtime_configuration":{},"type":"analyzer"},{"name":"FileScan_Search","status":"SUCCESS","report":{"count":5,"items":[{"id":"bc6d3a15-b371-41f6-8b53-57333fc779be","date":"06/30/2022, 01:16:19","file":{"name":"test.bat","sha256":"b636ee9b411b5cc6ea5fae704f0889d05f509b9642574136f086c23220ce951a","mime_type":"application/x-bat","short_type":null},"tags":[],"state":"success_partial","matches":[{"origin":{"sha256":"b636ee9b411b5cc6ea5fae704f0889d05f509b9642574136f086c23220ce951a","filetype":null,"relation":"source","mime_type":"application/x-bat"},"matches":{"ip":[{"value":"8.8.8.8"}]}}],"verdict":"informational","scan_init":{"id":"62bcf6c787c294f96f8b67e7"},"updated_date":"06/30/2022, 01:16:58"},{"id":"7d849b04-3f66-42d3-9059-0c3f0d3d6af5","date":"06/30/2022, 01:12:01","file":{"name":"Anyrun_port80.bat","sha256":"2b0411d9f1bdc2c3906710bd62cb35288e50ee774f845a7665dbc86a0d6be40d","mime_type":"application/x-bat","short_type":"html"},"tags":[{"tag":{"name":"html","verdict":{"verdict":"INFORMATIONAL","confidence":1,"threatLevel":0.1},"synonyms":[],"descriptions":[]},"source":"MEDIA_TYPE","isRootTag":true,"sourceIdentifier":"2b0411d9f1bdc2c3906710bd62cb35288e50ee774f845a7665dbc86a0d6be40d"}],"state":"success_partial","matches":[{"origin":{"sha256":"2b0411d9f1bdc2c3906710bd62cb35288e50ee774f845a7665dbc86a0d6be40d","filetype":null,"relation":"source","mime_type":"application/x-bat"},"matches":{"ip":[{"value":"8.8.8.8"}]}}],"verdict":"informational","scan_init":{"id":"62bcf6cab0578633c8b24d36"},"updated_date":"06/30/2022, 01:21:09"},{"id":"aed83c14-0c79-44b4-96c4-7da063a6fd30","date":"06/30/2022, 01:05:21","file":{"name":"Anyrun_port80.bat","sha256":"2b0411d9f1bdc2c3906710bd62cb35288e50ee774f845a7665dbc86a0d6be40d","mime_type":"application/x-bat","short_type":"html"},"tags":[{"tag":{"name":"html","verdict":{"verdict":"INFORMATIONAL","confidence":1,"threatLevel":0.1},"synonyms":[],"descriptions":[]},"source":"MEDIA_TYPE","isRootTag":true,"sourceIdentifier":"2b0411d9f1bdc2c3906710bd62cb35288e50ee774f845a7665dbc86a0d6be40d"}],"state":"success_partial","matches":[{"origin":{"sha256":"2b0411d9f1bdc2c3906710bd62cb35288e50ee774f845a7665dbc86a0d6be40d","filetype":null,"relation":"source","mime_type":"application/x-bat"},"matches":{"ip":[{"value":"8.8.8.8"}]}}],"verdict":"informational","scan_init":{"id":"62bcf6be95a8514e298d7edd"},"retry_count":1,"updated_date":"07/01/2022, 01:53:30"},{"id":"4fc5a2a8-5554-4b7f-bb2a-8a0056629aa9","date":"01/02/2022, 00:34:33","file":{"name":"6e1d9a9c12395e4b505e1606cc0a6e26446412cd","sha256":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899","mime_type":"application/vnd.ms-excel.sheet.macroenabled.12","short_type":"xlsx"},"tags":[{"tag":{"name":"xlsx","verdict":{"verdict":"INFORMATIONAL","confidence":1,"threatLevel":0.1},"synonyms":[],"descriptions":[]},"source":"MEDIA_TYPE","isRootTag":true,"sourceIdentifier":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899"},{"tag":{"name":"html","verdict":{"verdict":"INFORMATIONAL","confidence":1,"threatLevel":0.1},"synonyms":[],"descriptions":[]},"source":"MEDIA_TYPE","isRootTag":true,"sourceIdentifier":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899"},{"tag":{"name":"fingerprint","verdict":{"verdict":"LIKELY_MALICIOUS","confidence":1,"threatLevel":0.75},"synonyms":[],"descriptions":[]},"source":"SIGNAL","isRootTag":true,"sourceIdentifier":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899"},{"tag":{"name":"stealer","verdict":{"verdict":"LIKELY_MALICIOUS","confidence":1,"threatLevel":0.75},"synonyms":[],"descriptions":[]},"source":"SIGNAL","isRootTag":true,"sourceIdentifier":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899"},{"tag":{"name":"macros","verdict":{"verdict":"INFORMATIONAL","confidence":1,"threatLevel":0.1},"synonyms":[],"descriptions":[]},"source":"SIGNAL","isRootTag":true,"sourceIdentifier":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899"}],"state":"success_partial","matches":[{"origin":{"sha256":"a258701294dffe74d811d173db94ec6cad2227c792a4233cd1dc2124544b9899","filetype":"xlsx","relation":"source","mime_type":"application/vnd.ms-excel.sheet.macroenabled.12"},"matches":{"ip":[{"value":"8.8.8.8"}]}}],"verdict":"suspicious","scan_init":{"id":"61d0f2da4ab5c44cbf5b1f85"},"updated_date":"01/02/2022, 00:38:53"},{"id":"595a3779-d327-4ac9-81b4-eb793479cae6","date":"08/23/2021, 01:28:03","file":{"name":"king.bat","sha256":"3f285b9294040d32eaaff486866372b79f1236bfb300d9821d20024142831f05","mime_type":"application/x-bat","short_type":null},"tags":[],"state":"success_partial","matches":[{"origin":{"sha256":"3f285b9294040d32eaaff486866372b79f1236bfb300d9821d20024142831f05","filetype":null,"relation":"source","mime_type":"application/x-bat"},"matches":{"ip":[{"value":"8.8.8.8"}]}}],"verdict":"informational","scan_init":{"id":"6122aec2d972e521533a7b28"},"updated_date":"08/23/2021, 01:28:03"}],"query":"OC44LjguOA==","method":"and","count_search_params":1},"errors":[],"process_time":8.41,"start_time":"2022-07-15T20:25:47.665641Z","end_time":"2022-07-15T20:25:56.079799Z","runtime_configuration":{},"type":"analyzer"},{"name":"GreyNoiseCommunity","status":"SUCCESS","report":{"ip":"8.8.8.8","link":"https://viz.greynoise.io/riot/8.8.8.8","name":"Google APIs and Services","riot":true,"noise":false,"message":"Success","last_seen":"2022-07-15","classification":"benign"},"errors":[],"process_time":1.4,"start_time":"2022-07-15T20:25:48.982345Z","end_time":"2022-07-15T20:25:50.385093Z","runtime_configuration":{},"type":"analyzer"},{"name":"InQuest_IOCdb","status":"SUCCESS","report":{"data":[],"success":true},"errors":["No API key retrieved"],"process_time":1.68,"start_time":"2022-07-15T20:25:49.007793Z","end_time":"2022-07-15T20:25:50.691163Z","runtime_configuration":{},"type":"analyzer"},{"name":"GoogleWebRisk","status":"FAILED","report":{},"errors":["/opt/deploy/intel_owl/configuration/service_account_keyfile.json should be an existing file. Check the docs on how to add this file to properly execute this analyzer"],"process_time":0.09,"start_time":"2022-07-15T20:27:11.329940Z","end_time":"2022-07-15T20:27:11.420128Z","runtime_configuration":{},"type":"analyzer"}],"connector_reports":[],"permissions":{"kill":true,"delete":true,"plugin_actions":true},"is_sample":false,"md5":"40ff44d9e619b17524bf3763204f9cbb","observable_name":"8.8.8.8","observable_classification":"ip","file_name":"","file_mimetype":"","status":"reported_with_fails","analyzers_requested":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","GoogleWebRisk","FileScan_Search","InQuest_IOCdb","GreyNoiseAlpha","GreyNoiseCommunity"],"connectors_requested":[],"analyzers_to_execute":["Classic_DNS","CryptoScamDB_CheckAPI","Darksearch_Query","GoogleWebRisk","FileScan_Search","InQuest_IOCdb","GreyNoiseCommunity"],"connectors_to_execute":["YETI"],"received_request_time":"2022-07-15T20:25:44.041286Z","finished_analysis_time":"2022-07-15T20:27:11.909898Z","tlp":"WHITE","errors":[]}` + job := gointelowl.Job{} + err := json.Unmarshal([]byte(jobJsonString), &job) + if err != nil { + t.Fatalf("Unexpected error - could not parse job list json") + t.Fatalf("%v", err) + } else { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: 7, + Data: jobJsonString, + StatusCode: http.StatusOK, + Want: &job, + } + testCases["cantFind"] = TestData{ + Input: 9000, + Data: `{"detail":"Not found."}`, + StatusCode: http.StatusNotFound, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusNotFound, + Message: `{"detail":"Not found."}`, + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + id, ok := testCase.Input.(int) + if ok { + jobId := uint64(id) + testUrl := fmt.Sprintf(constants.SPECIFIC_JOB_URL, jobId) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET")) + gottenJob, err := client.JobService.Get(ctx, jobId) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenJob) + } + } else { + t.Fatalf("Casting failed!") + } + }) + } + } +} + +func TestJobServiceDownloadSample(t *testing.T) { + sampleString := "This is the sample" + doesNotHaveASampleResponseJsonString := `{"errors":{"detail":"Requested job does not have a sample associated with it."}}` + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: 1, + Data: sampleString, + StatusCode: http.StatusOK, + Want: []byte(sampleString), + } + testCases["doesNotHaveASample"] = TestData{ + Input: 2, + Data: doesNotHaveASampleResponseJsonString, + StatusCode: http.StatusBadRequest, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadRequest, + Message: doesNotHaveASampleResponseJsonString, + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + id, ok := testCase.Input.(int) + if ok { + jobId := uint64(id) + testUrl := fmt.Sprintf(constants.DOWNLOAD_SAMPLE_JOB_URL, jobId) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET")) + gottenSample, err := client.JobService.DownloadSample(ctx, jobId) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenSample) + } + } else { + t.Fatalf("Casting failed!") + } + }) + } +} + +func TestJobServiceDelete(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: 1, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + notFoundJson := `{"detail":"Not found."}` + testCases["notFound"] = TestData{ + Input: 300, + Data: notFoundJson, + StatusCode: http.StatusNotFound, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusNotFound, + Message: notFoundJson, + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + id, ok := testCase.Input.(int) + if ok { + jobId := uint64(id) + testUrl := fmt.Sprintf(constants.SPECIFIC_JOB_URL, jobId) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "DELETE")) + isDeleted, err := client.JobService.Delete(ctx, jobId) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, isDeleted) + } + } + }) + } +} + +func TestJobServiceKill(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: 1, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + testCases["notFound"] = TestData{ + Input: 300, + Data: `{"detail":"Not found."}`, + StatusCode: http.StatusNotFound, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusNotFound, + Message: `{"detail":"Not found."}`, + }, + } + testCases["jobNotRunning"] = TestData{ + Input: 71, + Data: `{"errors":{"detail":"Job is not running"}}`, + StatusCode: http.StatusBadRequest, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadRequest, + Message: `{"errors":{"detail":"Job is not running"}}`, + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + id, ok := testCase.Input.(int) + if ok { + jobId := uint64(id) + testUrl := fmt.Sprintf(constants.KILL_JOB_URL, jobId) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "PATCH")) + isDeleted, err := client.JobService.Kill(ctx, jobId) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, isDeleted) + } + } + }) + } +} + +type input struct { + Name string + Id uint64 +} + +func TestJobServiceKillAnalyzer(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: input{ + Name: "Phishstats", + Id: 71, + }, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + testCases["notFound"] = TestData{ + Input: input{ + Name: "NotAnAnalyzer", + Id: 71, + }, + Data: `{"errors":{"analyzer report":"Not found."}}`, + StatusCode: http.StatusNotFound, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusNotFound, + Message: `{"errors":{"analyzer report":"Not found."}}`, + }, + } + testCases["analyzerNotRunning"] = TestData{ + Input: input{ + Name: "Phishstats", + Id: 71, + }, + Data: `{"errors":{"detail":"Plugin call is not running or pending"}}`, + StatusCode: http.StatusBadRequest, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadRequest, + Message: `{"errors":{"detail":"Plugin call is not running or pending"}}`, + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + inputData, ok := testCase.Input.(input) + if ok { + testUrl := fmt.Sprintf(constants.KILL_ANALYZER_JOB_URL, inputData.Id, inputData.Name) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "PATCH")) + isDeleted, err := client.JobService.KillAnalyzer(ctx, inputData.Id, inputData.Name) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, isDeleted) + } + } + }) + } +} + +func TestJobServiceRetryAnalyzer(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: input{ + Name: "Phishstats", + Id: 71, + }, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + inputData, ok := testCase.Input.(input) + if ok { + testUrl := fmt.Sprintf(constants.RETRY_ANALYZER_JOB_URL, inputData.Id, inputData.Name) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "PATCH")) + retry, err := client.JobService.RetryAnalyzer(ctx, inputData.Id, inputData.Name) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, retry) + } + } + }) + } +} + +func TestJobServiceKillConnector(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: input{ + Name: "YETI", + Id: 71, + }, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + inputData, ok := testCase.Input.(input) + if ok { + testUrl := fmt.Sprintf(constants.KILL_CONNECTOR_JOB_URL, inputData.Id, inputData.Name) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "PATCH")) + isDeleted, err := client.JobService.KillConnector(ctx, inputData.Id, inputData.Name) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, isDeleted) + } + } + }) + } +} + +func TestJobServiceRetryConnector(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: input{ + Name: "YETI", + Id: 71, + }, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + inputData, ok := testCase.Input.(input) + if ok { + testUrl := fmt.Sprintf(constants.RETRY_CONNECTOR_JOB_URL, inputData.Id, inputData.Name) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "PATCH")) + retry, err := client.JobService.RetryConnector(ctx, inputData.Id, inputData.Name) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, retry) + } + } + }) + } +} diff --git a/Submodules/GoIntelOwl/tests/tagService_test.go b/Submodules/GoIntelOwl/tests/tagService_test.go new file mode 100644 index 0000000..671ac1f --- /dev/null +++ b/Submodules/GoIntelOwl/tests/tagService_test.go @@ -0,0 +1,227 @@ +package tests + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/intelowlproject/go-intelowl/constants" + "github.com/intelowlproject/go-intelowl/gointelowl" +) + +// * Test for TagService.List method! +func TestTagServiceList(t *testing.T) { + + testCases := make(map[string]TestData) + + testCases["simple"] = TestData{ + Input: nil, + Data: `[ + {"id": 1,"label": "TEST1","color": "#1c71d8"}, + {"id": 2,"label": "TEST2","color": "#1c71d7"}, + {"id": 3,"label": "TEST3","color": "#1c71d6"} + ]`, + StatusCode: http.StatusOK, + Want: []gointelowl.Tag{ + { + ID: 1, + Label: "TEST1", + Color: "#1c71d8", + }, + { + ID: 2, + Label: "TEST2", + Color: "#1c71d7", + }, + { + ID: 3, + Label: "TEST3", + Color: "#1c71d6", + }, + }, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + apiHandler.Handle(constants.BASE_TAG_URL, serverHandler(t, testCase, "GET")) + ctx := context.Background() + gottenTagList, err := client.TagService.List(ctx) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, (*gottenTagList)) + } + }) + } +} + +func TestTagServiceGet(t *testing.T) { + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: 1, + StatusCode: http.StatusOK, + Data: `{"id": 1,"label": "TEST","color": "#1c71d8"}`, + Want: &gointelowl.Tag{ + ID: 1, + Label: "TEST", + Color: "#1c71d8", + }, + } + testCases["cantFind"] = TestData{ + Input: 9000, + StatusCode: http.StatusNotFound, + Data: `{"detail": "Not found."}`, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusNotFound, + Message: `{"detail": "Not found."}`, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + id, ok := testCase.Input.(int) + if ok { + tagId := uint64(id) + testUrl := fmt.Sprintf(constants.SPECIFIC_TAG_URL, tagId) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "GET")) + gottenTag, err := client.TagService.Get(ctx, tagId) + // Helper test to check error + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenTag) + } + } else { + t.Fatalf("Casting failed!") + t.Fatalf("You didn't pass the correct Input datatype") + } + }) + } +} + +func TestTagServiceCreate(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: gointelowl.TagParams{ + Label: "TEST TAG", + Color: "#fffff", + }, + Data: `{"id": 1,"label": "TEST TAG","color": "#fffff"}`, + StatusCode: http.StatusOK, + Want: &gointelowl.Tag{ + ID: 1, + Label: "TEST TAG", + Color: "#fffff", + }, + } + testCases["duplicate"] = TestData{ + Input: gointelowl.TagParams{ + Label: "TEST TAG", + Color: "#fffff", + }, + Data: `{"label":["tag with this label already exists."]}`, + StatusCode: http.StatusBadRequest, + Want: &gointelowl.IntelOwlError{ + StatusCode: http.StatusBadRequest, + Message: `{"label":["tag with this label already exists."]}`, + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.BASE_TAG_URL, serverHandler(t, testCase, "POST")) + tagParams, ok := testCase.Input.(gointelowl.TagParams) + if ok { + gottenTag, err := client.TagService.Create(ctx, &tagParams) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenTag) + } + } else { + t.Fatalf("Casting failed!") + } + }) + } +} + +func TestTagServiceUpdate(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: gointelowl.Tag{ + ID: 1, + Label: "UPDATED TEST TAG", + Color: "#f4", + }, + Data: `{"id": 1,"label": "UPDATED TEST TAG","color": "#f4"}`, + StatusCode: http.StatusOK, + Want: &gointelowl.Tag{ + ID: 1, + Label: "UPDATED TEST TAG", + Color: "#f4", + }, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + tag, ok := testCase.Input.(gointelowl.Tag) + if ok { + testUrl := fmt.Sprintf(constants.SPECIFIC_TAG_URL, tag.ID) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "PUT")) + gottenTag, err := client.TagService.Update(ctx, tag.ID, &gointelowl.TagParams{ + Label: tag.Label, + Color: tag.Color, + }) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenTag) + } + } + }) + } +} + +func TestTagServiceDelete(t *testing.T) { + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: 1, + Data: "", + StatusCode: http.StatusNoContent, + Want: true, + } + for name, testCase := range testCases { + //* Subtest + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + id, ok := testCase.Input.(int) + if ok { + tagId := uint64(id) + testUrl := fmt.Sprintf(constants.SPECIFIC_TAG_URL, tagId) + apiHandler.Handle(testUrl, serverHandler(t, testCase, "DELETE")) + isDeleted, err := client.TagService.Delete(ctx, tagId) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, isDeleted) + } + } + }) + } +} diff --git a/Submodules/GoIntelOwl/tests/testFiles/fileForAnalysis.txt b/Submodules/GoIntelOwl/tests/testFiles/fileForAnalysis.txt new file mode 100644 index 0000000..a45c82e --- /dev/null +++ b/Submodules/GoIntelOwl/tests/testFiles/fileForAnalysis.txt @@ -0,0 +1 @@ +1st test File \ No newline at end of file diff --git a/Submodules/GoIntelOwl/tests/testFiles/fileForAnalysis2.txt b/Submodules/GoIntelOwl/tests/testFiles/fileForAnalysis2.txt new file mode 100644 index 0000000..92eb0d5 --- /dev/null +++ b/Submodules/GoIntelOwl/tests/testFiles/fileForAnalysis2.txt @@ -0,0 +1 @@ +2nd test File \ No newline at end of file diff --git a/Submodules/GoIntelOwl/tests/userService_test.go b/Submodules/GoIntelOwl/tests/userService_test.go new file mode 100644 index 0000000..36c00cf --- /dev/null +++ b/Submodules/GoIntelOwl/tests/userService_test.go @@ -0,0 +1,171 @@ +package tests + +import ( + "context" + "encoding/json" + "net/http" + "testing" + + "github.com/intelowlproject/go-intelowl/constants" + "github.com/intelowlproject/go-intelowl/gointelowl" +) + +func TestUserServiceAccess(t *testing.T) { + userStringJson := `{"user":{"username":"hussain","first_name":"h","last_name":"k","full_name":"h k","email":"mshk9991@gmail.com"},"access":{"total_submissions":38,"month_submissions":28}}` + userResponse := &gointelowl.User{} + if unmarshalError := json.Unmarshal([]byte(userStringJson), &userResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + // table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: nil, + Data: userStringJson, + StatusCode: http.StatusOK, + Want: userResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + apiHandler.Handle(constants.USER_DETAILS_URL, serverHandler(t, testCase, "GET")) + ctx := context.Background() + gottenUserResponse, err := client.UserService.Access(ctx) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenUserResponse) + } + }) + } +} + +func TestUserServiceOrganization(t *testing.T) { + orgRespJsonStr := `{"members_count":1,"owner":{"username":"hussain","full_name":"h k","joined":"2022-07-23T09:11:08.674294Z"},"is_user_owner":true,"created_at":"2022-07-23T09:11:08.580533Z","name":"StrawHats"}` + orgResponse := &gointelowl.Organization{} + if unmarshalError := json.Unmarshal([]byte(orgRespJsonStr), &orgResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + // *table test case + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: nil, + Data: orgRespJsonStr, + StatusCode: http.StatusOK, + Want: orgResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + apiHandler.Handle(constants.ORGANIZATION_URL, serverHandler(t, testCase, "GET")) + ctx := context.Background() + gottenUserResponse, err := client.UserService.Organization(ctx) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenUserResponse) + } + }) + } +} + +func TestUserServiceCreateOrganization(t *testing.T) { + orgRespJsonStr := `{"members_count":1,"owner":{"username":"notHussain","full_name":"noy Hussain","joined":"2022-07-24T17:34:55.032629Z"},"is_user_owner":true,"created_at":"2022-07-24T17:34:54.971735Z","name":"TestOrganization"}` + orgResponse := &gointelowl.Organization{} + if unmarshalError := json.Unmarshal([]byte(orgRespJsonStr), &orgResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + orgParams := gointelowl.OrganizationParams{ + Name: "TestOrganization", + } + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: orgParams, + Data: orgRespJsonStr, + StatusCode: http.StatusOK, + Want: orgResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.ORGANIZATION_URL, serverHandler(t, testCase, "POST")) + params, ok := testCase.Input.(gointelowl.OrganizationParams) + if ok { + gottenOrgResponse, err := client.UserService.CreateOrganization(ctx, ¶ms) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenOrgResponse) + } + } + }) + } +} + +func TestUserServiceRemoveMemberFromOrganization(t *testing.T) { + memberParams := gointelowl.MemberParams{ + Username: "TestUser", + } + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: memberParams, + Data: ``, + StatusCode: http.StatusNoContent, + Want: true, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle(constants.REMOVE_MEMBER_FROM_ORGANIZATION_URL, serverHandler(t, testCase, "POST")) + params, ok := testCase.Input.(gointelowl.MemberParams) + if ok { + left, err := client.UserService.RemoveMemberFromOrganization(ctx, ¶ms) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, left) + } + } + }) + } +} + +func TestUserServiceInviteToOrganization(t *testing.T) { + inviteJsonStr := `{"id":12,"created_at":"2022-07-24T18:43:42.299318Z","status":"pending"}` + inviteResponse := &gointelowl.Invite{} + if unmarshalError := json.Unmarshal([]byte(inviteJsonStr), &inviteResponse); unmarshalError != nil { + t.Fatalf("Error: %s", unmarshalError) + } + memberParams := gointelowl.MemberParams{ + Username: "TestUser", + } + testCases := make(map[string]TestData) + testCases["simple"] = TestData{ + Input: memberParams, + Data: inviteJsonStr, + StatusCode: http.StatusCreated, + Want: inviteResponse, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + client, apiHandler, closeServer := setup() + defer closeServer() + ctx := context.Background() + apiHandler.Handle("/api/me/organization/invite", serverHandler(t, testCase, "POST")) + params, ok := testCase.Input.(gointelowl.MemberParams) + if ok { + gottenInviteResponse, err := client.UserService.InviteToOrganization(ctx, ¶ms) + if err != nil { + testError(t, testCase, err) + } else { + testWantData(t, testCase.Want, gottenInviteResponse) + } + } + }) + } +} diff --git a/Submodules/GreedyBear/FEEDS_LICENSE/index.html b/Submodules/GreedyBear/FEEDS_LICENSE/index.html new file mode 100644 index 0000000..43ec532 --- /dev/null +++ b/Submodules/GreedyBear/FEEDS_LICENSE/index.html @@ -0,0 +1,589 @@ + + + + + + + + + + + +FEEDS LICENSE - IntelOwl Documentation + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GreedyBear/LICENSE b/Submodules/GreedyBear/LICENSE new file mode 100644 index 0000000..d3b7ebe --- /dev/null +++ b/Submodules/GreedyBear/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 The Honeynet Project + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Submodules/GreedyBear/api/__init__.py b/Submodules/GreedyBear/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/GreedyBear/api/admin.py b/Submodules/GreedyBear/api/admin.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/GreedyBear/api/apps.py b/Submodules/GreedyBear/api/apps.py new file mode 100644 index 0000000..cbe2c17 --- /dev/null +++ b/Submodules/GreedyBear/api/apps.py @@ -0,0 +1,7 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + name = "api" diff --git a/Submodules/GreedyBear/api/serializers.py b/Submodules/GreedyBear/api/serializers.py new file mode 100644 index 0000000..937ce0f --- /dev/null +++ b/Submodules/GreedyBear/api/serializers.py @@ -0,0 +1,83 @@ +import logging +import re + +from greedybear.consts import REGEX_DOMAIN, REGEX_IP +from greedybear.models import IOC, GeneralHoneypot +from rest_framework import serializers + +logger = logging.getLogger(__name__) + + +class GeneralHoneypotSerializer(serializers.ModelSerializer): + class Meta: + model = GeneralHoneypot + + def to_representation(self, value): + return value.name + + +class IOCSerializer(serializers.ModelSerializer): + general_honeypot = GeneralHoneypotSerializer(many=True, read_only=True) + + class Meta: + model = IOC + exclude = [ + "related_urls", + ] + + +class EnrichmentSerializer(serializers.Serializer): + found = serializers.BooleanField(read_only=True, default=False) + ioc = IOCSerializer(read_only=True, default=None) + query = serializers.CharField(max_length=250) + + def validate(self, data): + """ + Check a given observable against regex expression + """ + observable = data["query"] + if not re.match(REGEX_IP, observable) or not re.match(REGEX_DOMAIN, observable): + raise serializers.ValidationError("Observable is not a valid IP or domain") + try: + required_object = IOC.objects.get(name=observable) + data["found"] = True + data["ioc"] = required_object + except IOC.DoesNotExist: + data["found"] = False + return data + + +def feed_type_validation(feed_type): + feed_choices = ["log4j", "cowrie", "all"] + generalHoneypots = GeneralHoneypot.objects.all().filter(active=True) + feed_choices.extend([hp.name.lower() for hp in generalHoneypots]) # FEEDS + + if feed_type not in feed_choices: + logger.info(f"Feed type {feed_type} not in feed_choices {feed_choices}") + raise serializers.ValidationError("Invalid feed_type") + return feed_type + + +class FeedsSerializer(serializers.Serializer): + feed_type = serializers.CharField(max_length=120) + attack_type = serializers.ChoiceField(choices=["scanner", "payload_request", "all"]) + age = serializers.ChoiceField(choices=["persistent", "recent"]) + format = serializers.ChoiceField(choices=["csv", "json", "txt"], default="json") + + def validate_feed_type(self, feed_type): + logger.debug(f"FeedsSerializer - Validation feed_type: '{feed_type}'") + return feed_type_validation(feed_type) + + +class FeedsResponseSerializer(serializers.Serializer): + feed_type = serializers.CharField(max_length=120) + value = serializers.CharField(max_length=120) + scanner = serializers.BooleanField() + payload_request = serializers.BooleanField() + first_seen = serializers.DateField(format="%Y-%m-%d") + last_seen = serializers.DateField(format="%Y-%m-%d") + times_seen = serializers.IntegerField() + + def validate_feed_type(self, feed_type): + logger.debug(f"FeedsResponseSerializer - validation feed_type: '{feed_type}'") + return feed_type_validation(feed_type) diff --git a/Submodules/GreedyBear/api/urls.py b/Submodules/GreedyBear/api/urls.py new file mode 100644 index 0000000..5ae8890 --- /dev/null +++ b/Submodules/GreedyBear/api/urls.py @@ -0,0 +1,24 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. +from api.views import StatisticsViewSet, enrichment_view, feeds, feeds_pagination, general_honeypot_list +from django.urls import include, path +from rest_framework import routers + +# Routers provide an easy way of automatically determining the URL conf. +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"statistics", StatisticsViewSet, basename="statistics") + +# These come after /api/.. +urlpatterns = [ + path("feeds/", feeds_pagination), + path("feeds///.", feeds), + path("enrichment", enrichment_view), + path("general_honeypot", general_honeypot_list), + # router viewsets + path("", include(router.urls)), + # certego_saas: + # default apps (user), + path("", include("certego_saas.urls")), + # auth + path("auth/", include("authentication.urls")), +] diff --git a/Submodules/GreedyBear/api/views.py b/Submodules/GreedyBear/api/views.py new file mode 100644 index 0000000..3094f7b --- /dev/null +++ b/Submodules/GreedyBear/api/views.py @@ -0,0 +1,301 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. +import csv +import logging +from datetime import datetime, timedelta + +from api.serializers import EnrichmentSerializer, FeedsResponseSerializer, FeedsSerializer, IOCSerializer +from certego_saas.apps.auth.backend import CookieTokenAuthentication +from certego_saas.ext.helpers import parse_humanized_range +from certego_saas.ext.pagination import CustomPageNumberPagination +from django.db.models import Count, Q +from django.db.models.functions import Trunc +from django.http import HttpResponse, HttpResponseServerError, StreamingHttpResponse +from drf_spectacular.utils import extend_schema as add_docs +from drf_spectacular.utils import inline_serializer +from greedybear.consts import FEEDS_LICENSE, GET, PAYLOAD_REQUEST, SCANNER +from greedybear.models import IOC, GeneralHoneypot, Statistics, viewType +from rest_framework import serializers as rfs +from rest_framework import status, viewsets +from rest_framework.decorators import action, api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response + +logger = logging.getLogger(__name__) + + +class Echo: + """An object that implements just the write method of the file-like + interface. + """ + + def write(self, value): + """Write the value by returning it, instead of storing in a buffer.""" + return value + + +# The Doc does not work as intended. We should refactor this by correctly leveraging DRF +@add_docs(description="Extract Structured IOC Feeds from GreedyBear") +@api_view([GET]) +def feeds(request, feed_type, attack_type, age, format_): + logger.info(f"request /api/feeds with params: feed type: {feed_type}, attack_type: {attack_type}, Age: {age}, format: {format_}") + iocs_queryset = get_queryset(request, feed_type, attack_type, age, format_) + return feeds_response(request, iocs_queryset, feed_type, format_) + + +@api_view([GET]) +def feeds_pagination(request): + params = request.query_params + logger.info(f"request /api/feeds with params: {params}") + + paginator = CustomPageNumberPagination() + iocs_queryset = get_queryset(request, params["feed_type"], params["attack_type"], params["age"], "json") + iocs = paginator.paginate_queryset(iocs_queryset, request) + resp_data = feeds_response(request, iocs, params["feed_type"], "json", dict_only=True) + return paginator.get_paginated_response(resp_data) + + +def get_queryset(request, feed_type, attack_type, age, format_): + source = str(request.user) + logger.info(f"request from {source}. Feed type: {feed_type}, attack_type: {attack_type}, Age: {age}, format: {format_}") + + serializer = FeedsSerializer(data={"feed_type": feed_type, "attack_type": attack_type, "age": age, "format": format_}) + serializer.is_valid(raise_exception=True) + + ordering = request.query_params.get("ordering") + # if ordering == "value" replace it with "name" (the corresponding field in the iocs model) + if ordering == "value": + ordering = "name" + elif ordering == "-value": + ordering = "-name" + + query_dict = {} + + if feed_type != "all": + if feed_type == "log4j" or feed_type == "cowrie": + query_dict[feed_type] = True + else: + # accept feed_type if it is in the general honeypots list + query_dict["general_honeypot__name__iexact"] = feed_type + + if attack_type != "all": + query_dict[attack_type] = True + + if age == "recent": + # everything in the last 3 days + three_days_ago = datetime.utcnow() - timedelta(days=3) + query_dict["last_seen__gte"] = three_days_ago + # if ordering == "feed_type" or None replace it with the default value "-last_seen" + # ordering by "feed_type" is done in feed_response function + if ordering is None or ordering == "feed_type" or ordering == "-feed_type": + ordering = "-last_seen" + iocs = IOC.objects.exclude(general_honeypot__active=False).filter(**query_dict).order_by(ordering)[:5000] + elif age == "persistent": + # scanners detected in the last 14 days + fourteen_days_ago = datetime.utcnow() - timedelta(days=14) + query_dict["last_seen__gte"] = fourteen_days_ago + # ... and at least detected 10 different days + number_of_days_seen = 10 + query_dict["number_of_days_seen__gte"] = number_of_days_seen + # if ordering == "feed_type" or None replace it with the default value "-times_seen" + # ordering by "feed_type" is done in feed_response function + if ordering is None or ordering == "feed_type" or ordering == "-feed_type": + ordering = "-times_seen" + iocs = IOC.objects.exclude(general_honeypot__active=False).filter(**query_dict).order_by(ordering)[:5000] + + # save request source for statistics + source_ip = str(request.META["REMOTE_ADDR"]) + request_source = Statistics(source=source_ip) + request_source.save() + + logger.info(f"Number of iocs returned: {len(iocs)}") + return iocs + + +def feeds_response(request, iocs, feed_type, format_, dict_only=False): + logger.info(f"Format feeds in: {format_}") + license_text = ( + f"# These feeds are generated by The Honeynet Project" f" once every 10 minutes and are protected" f" by the following license: {FEEDS_LICENSE}" + ) + + if format_ == "txt": + text_lines = [license_text] + for ioc in iocs: + text_lines.append(ioc.name) + text = "\n".join(text_lines) + return HttpResponse(text, content_type="text/plain") + elif format_ == "csv": + rows = [] + rows.append([license_text]) + for ioc in iocs: + rows.append([ioc.name]) + pseudo_buffer = Echo() + writer = csv.writer(pseudo_buffer, quoting=csv.QUOTE_NONE) + return StreamingHttpResponse( + (writer.writerow(row) for row in rows), + content_type="text/csv", + headers={"Content-Disposition": 'attachment; filename="feeds.csv"'}, + status=200, + ) + elif format_ == "json": + # json + json_list = [] + ioc_feed_type = "" + for ioc in iocs: + if feed_type not in ["all", "log4j", "cowrie"]: + ioc_feed_type = feed_type + else: + if ioc.log4j: + ioc_feed_type = "log4j" + elif ioc.cowrie: + ioc_feed_type = "cowrie" + else: + ioc_feed_type = str(ioc.general_honeypot.first()).lower() + + data_ = { + "value": ioc.name, + SCANNER: ioc.scanner, + PAYLOAD_REQUEST: ioc.payload_request, + "first_seen": ioc.first_seen.strftime("%Y-%m-%d"), + "last_seen": ioc.last_seen.strftime("%Y-%m-%d"), + "times_seen": ioc.times_seen, + "feed_type": ioc_feed_type, + } + + serializer_item = FeedsResponseSerializer(data=data_) + serializer_item.is_valid(raise_exception=True) + json_list.append(serializer_item.data) + + # check if sorting the results by feed_type + ordering = request.query_params.get("ordering") + sorted_list = [] + if ordering == "feed_type": + sorted_list = sorted(json_list, key=lambda k: k["feed_type"]) + elif ordering == "-feed_type": + sorted_list = sorted(json_list, key=lambda k: k["feed_type"], reverse=True) + + if sorted_list: + logger.info("Return feeds sorted by feed_type field") + json_list = sorted_list + + logger.info(f"Number of feeds returned: {len(json_list)}") + resp_data = {"license": FEEDS_LICENSE, "iocs": json_list} + if dict_only: + return resp_data + else: + return Response(resp_data, status=status.HTTP_200_OK) + + +# The Doc does not work as intended. We should refactor this by correctly leveraging DRF +@add_docs( + description="Request if a specific observable (domain or IP address) has been listed by GreedyBear", + request=inline_serializer( + name="EnrichmentSerializerRequest", + fields={"query": rfs.CharField()}, + ), + responses={ + 200: inline_serializer( + name="EnrichmentSerializerResponse", + fields={"found": rfs.BooleanField(), "ioc": IOCSerializer}, + ), + }, +) +@api_view([GET]) +@authentication_classes([CookieTokenAuthentication]) +@permission_classes([IsAuthenticated]) +def enrichment_view(request): + observable_name = request.query_params.get("query") + logger.info(f"Enrichment view requested for: {str(observable_name)}") + serializer = EnrichmentSerializer(data=request.query_params, context={"request": request}) + serializer.is_valid(raise_exception=True) + + source_ip = str(request.META["REMOTE_ADDR"]) + request_source = Statistics(source=source_ip, view=viewType.ENRICHMENT_VIEW.value) + request_source.save() + + return Response(serializer.data, status=status.HTTP_200_OK) + + +class StatisticsViewSet(viewsets.ViewSet): + @action(detail=True, methods=["GET"]) + def feeds(self, request, pk=None): + if pk == "sources": + annotations = {"Sources": Count("source", distinct=True, filter=Q(view=viewType.FEEDS_VIEW.value))} + elif pk == "downloads": + annotations = {"Downloads": Count("source", filter=Q(view=viewType.FEEDS_VIEW.value))} + else: + logger.error("this is impossible. check the code") + return HttpResponseServerError() + return self.__aggregation_response_static_statistics(annotations) + + @action(detail=True, methods=["get"]) + def enrichment(self, request, pk=None): + if pk == "sources": + annotations = { + "Sources": Count( + "source", + distinct=True, + filter=Q(view=viewType.ENRICHMENT_VIEW.value), + ) + } + elif pk == "requests": + annotations = {"Requests": Count("source", filter=Q(view=viewType.ENRICHMENT_VIEW.value))} + else: + logger.error("this is impossible. check the code") + return HttpResponseServerError() + return self.__aggregation_response_static_statistics(annotations) + + @action(detail=False, methods=["get"]) + def feeds_types(self, request): + # FEEDS + annotations = { + "Log4j": Count("name", distinct=True, filter=Q(log4j=True)), + "Cowrie": Count("name", distinct=True, filter=Q(cowrie=True)), + } + # feed_type for each general honeypot in the list + generalHoneypots = GeneralHoneypot.objects.all().filter(active=True) + for hp in generalHoneypots: + annotations[hp.name] = Count("name", Q(general_honeypot__name__iexact=hp.name.lower())) + return self.__aggregation_response_static_ioc(annotations) + + def __aggregation_response_static_statistics(self, annotations: dict) -> Response: + delta, basis = self.__parse_range(self.request) + qs = Statistics.objects.filter(request_date__gte=delta).annotate(date=Trunc("request_date", basis)).values("date").annotate(**annotations) + return Response(qs) + + def __aggregation_response_static_ioc(self, annotations: dict) -> Response: + delta, basis = self.__parse_range(self.request) + + qs = ( + IOC.objects.filter(last_seen__gte=delta) + .exclude(general_honeypot__active=False) + .annotate(date=Trunc("last_seen", basis)) + .values("date") + .annotate(**annotations) + ) + return Response(qs) + + @staticmethod + def __parse_range(request): + try: + range_str = request.GET["range"] + except KeyError: + # default + range_str = "7d" + + return parse_humanized_range(range_str) + + +@api_view([GET]) +def general_honeypot_list(request): + logger.info(f"Requested general honeypots list from {request.user}.") + active = request.query_params.get("onlyActive") + honeypots = [] + generalHoneypots = GeneralHoneypot.objects.all() + if active == "true": + generalHoneypots = generalHoneypots.filter(active=True) + logger.info("Requested only active general honeypots") + honeypots.extend([hp.name for hp in generalHoneypots]) + + logger.info(f"General honeypots: {honeypots}") + return Response(honeypots) diff --git a/Submodules/GreedyBear/authentication/__init__.py b/Submodules/GreedyBear/authentication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/GreedyBear/authentication/admin.py b/Submodules/GreedyBear/authentication/admin.py new file mode 100644 index 0000000..fdd1e3b --- /dev/null +++ b/Submodules/GreedyBear/authentication/admin.py @@ -0,0 +1,171 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. +from typing import Optional + +import email_utils +from certego_saas.apps.user.admin import AbstractUserAdmin +from certego_saas.apps.user.models import User +from django.conf import settings +from django.contrib import admin, messages +from django.db.models import Q +from django.utils.translation import ngettext +from durin.admin import AuthTokenAdmin +from durin.models import AuthToken, Client + +from .models import UserProfile + +__all__ = ["UserAdminView", "UserProfileAdmin"] + + +class UserProfileInlineAdmin(admin.StackedInline): + model = UserProfile + + +# certego-saas +@admin.register(User) +class UserAdminView(AbstractUserAdmin): + inlines = (UserProfileInlineAdmin,) + list_display = ( + "username", + "email", + "first_name", + "last_name", + "is_active", + "approved", + "is_email_verified", + "is_staff", + ) + actions = ["accept_users", "decline_users"] + + @admin.display(boolean=True) + def is_email_verified(self, obj: User) -> Optional[bool]: + return obj.is_email_verified + + @admin.action(description="Decline selected users") + def decline_users(self, request, queryset): + # 1. user email may or may not be verified + # 2. we can not decline users that have been already accept + declinable = Q(is_active=False) & Q(approved=None) + users = queryset.filter(declinable).all() + for user in users: + email_utils.send_email( + from_email=settings.DEFAULT_FROM_EMAIL, + recipient_list=[user.email], + subject="GreedyBear - Your account request has been declined", + template_name="authentication/emails/account-declined", + context={ + "full_name": user.get_full_name(), + "username": user.get_username(), + "host_uri": settings.HOST_URI, + "host_name": settings.HOST_NAME, + "default_email": settings.DEFAULT_EMAIL, + }, + ) + number_declined = users.update(approved=False, is_active=False) + self.message_user( + request, + ngettext( + "%d user was declined.", + "%d users were declined.", + number_declined, + ) + % number_declined, + messages.SUCCESS, + ) + + @admin.action(description="Accept selected users") + def accept_users(self, request, queryset): + # 1. user email should be verified + # 2. we can accept previously declined users + acceptable = Q(email_address__is_verified=True) & Q(is_active=False) & (Q(approved=False) | Q(approved=None)) + users = queryset.filter(acceptable).all() + for user in users: + email_utils.send_email( + from_email=settings.DEFAULT_FROM_EMAIL, + recipient_list=[user.email], + subject="GreedyBear - Your account has been successfully activated!", + template_name="authentication/emails/account-activated", + context={ + "full_name": user.get_full_name(), + "username": user.get_username(), + "host_uri": settings.HOST_URI, + "host_name": settings.HOST_NAME, + "default_email": settings.DEFAULT_EMAIL, + }, + ) + number_updated = users.update(is_active=True, approved=True) + self.message_user( + request, + ngettext( + "%d user was successfully activated.", + "%d users were successfully activated.", + number_updated, + ) + % number_updated, + messages.SUCCESS, + ) + + +@admin.register(UserProfile) +class UserProfileAdmin(admin.ModelAdmin): + list_select_related = ("user",) + list_display = ( + "user", + "user_is_active", + "user_is_approved", + "twitter_handle", + "company_name", + "company_role", + "discover_from", + ) + + @admin.display(boolean=True) + def user_is_active(self, obj: UserProfile) -> bool: + return obj.user.is_active + + @admin.display(boolean=True) + def user_is_approved(self, obj: UserProfile) -> Optional[bool]: + return obj.user.approved + + +# durin app (AuthToken model) customization + + +class CustomAuthTokenAdmin(AuthTokenAdmin): + """ + Custom admin view for AuthToken model + """ + + exclude = [] + raw_id_fields = ("user",) + readonly_fields = ("token", "expiry", "created", "expires_in") + + def get_fieldsets(self, request, obj=None): + if not obj: + return [ + ( + "Create token for PyGreedyBear", + { + "fields": ("user",), + "description": """ +

Token will be auto-generated on save.

+

This token will be valid for 10 years.

+ """, + }, + ), + ] + return super().get_fieldsets(request, obj) + + @staticmethod + def has_change_permission(*args, **kwargs): + return False + + def save_model(self, request, obj, form, change): + obj.client = Client.objects.get(name=settings.REST_DURIN["API_ACCESS_CLIENT_NAME"]) + super().save_model(request, obj, form, change) + + +# Unregister the default admin view for AuthToken +admin.site.unregister(AuthToken) +# Register our custom admin view for AuthToken +admin.site.register(AuthToken, CustomAuthTokenAdmin) diff --git a/Submodules/GreedyBear/authentication/apps.py b/Submodules/GreedyBear/authentication/apps.py new file mode 100644 index 0000000..c65f1d2 --- /dev/null +++ b/Submodules/GreedyBear/authentication/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthenticationConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "authentication" diff --git a/Submodules/GreedyBear/authentication/migrations/0001_initial.py b/Submodules/GreedyBear/authentication/migrations/0001_initial.py new file mode 100644 index 0000000..42f67e2 --- /dev/null +++ b/Submodules/GreedyBear/authentication/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 3.2.18 on 2023-03-22 16:14 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="UserProfile", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("company_name", models.CharField(max_length=32, validators=[django.core.validators.MinLengthValidator(3)])), + ("company_role", models.CharField(max_length=32, validators=[django.core.validators.MinLengthValidator(3)])), + ("twitter_handle", models.CharField(blank=True, default="", max_length=16, validators=[django.core.validators.MinLengthValidator(3)])), + ( + "discover_from", + models.CharField( + choices=[ + ("search_engine", "Search Engine (Google, DuckDuckGo, etc.)"), + ("was_recommended", "Recommended by friend or colleague"), + ("social_media", "Social media"), + ("blog_or_publication", "Blog or Publication"), + ("other", "Other"), + ], + default="other", + max_length=32, + ), + ), + ("user", models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name="user_profile", to=settings.AUTH_USER_MODEL)), + ], + options={ + "verbose_name_plural": "User Profiles", + }, + ), + ] diff --git a/Submodules/GreedyBear/authentication/migrations/__init__.py b/Submodules/GreedyBear/authentication/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/GreedyBear/authentication/models.py b/Submodules/GreedyBear/authentication/models.py new file mode 100644 index 0000000..f9806f2 --- /dev/null +++ b/Submodules/GreedyBear/authentication/models.py @@ -0,0 +1,42 @@ +from django.conf import settings +from django.core.validators import MinLengthValidator +from django.db import models + +__all__ = [ + "UserProfile", +] + + +# constants +class DiscoverFromChoices(models.TextChoices): + SEARCH_ENGINE = "search_engine", "Search Engine (Google, DuckDuckGo, etc.)" + WAS_RECOMMENDED = "was_recommended", "Recommended by friend or colleague" + SOCIAL_MEDIA = "social_media", "Social media" + BLOG_OR_PUBLICATION = "blog_or_publication", "Blog or Publication" + OTHER = "other", "Other" + + +# models +class UserProfile(models.Model): + # meta + class Meta: + verbose_name_plural = "User Profiles" + + # contants + DiscoverFromChoices = DiscoverFromChoices + + # fields + + user = models.OneToOneField( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="user_profile", + ) + company_name = models.CharField(max_length=32, validators=[MinLengthValidator(3)]) + company_role = models.CharField(max_length=32, validators=[MinLengthValidator(3)]) + twitter_handle = models.CharField(max_length=16, default="", blank=True, validators=[MinLengthValidator(3)]) + discover_from = models.CharField( + max_length=32, + choices=DiscoverFromChoices.choices, + default=DiscoverFromChoices.OTHER, + ) diff --git a/Submodules/GreedyBear/authentication/serializers.py b/Submodules/GreedyBear/authentication/serializers.py new file mode 100644 index 0000000..78107c9 --- /dev/null +++ b/Submodules/GreedyBear/authentication/serializers.py @@ -0,0 +1,164 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. +import logging +import re + +import rest_email_auth.serializers +from certego_saas.ext.upload import Slack +from certego_saas.models import User +from certego_saas.settings import certego_apps_settings +from django.conf import settings +from django.contrib.auth import password_validation +from django.core.exceptions import ValidationError +from django.db import DatabaseError, transaction +from django.utils.translation import gettext_lazy as _ +from greedybear.consts import REGEX_PASSWORD +from rest_framework import serializers as rfs +from rest_framework.authtoken.serializers import AuthTokenSerializer +from slack_sdk.errors import SlackApiError + +from .models import UserProfile + +logger = logging.getLogger(__name__) + +__all__ = [ + "UserProfileSerializer", + "RegistrationSerializer", + "EmailVerificationSerializer", +] + + +class UserProfileSerializer(rfs.ModelSerializer): + class Meta: + model = UserProfile + exclude = ("user",) + + +class RegistrationSerializer(rest_email_auth.serializers.RegistrationSerializer): + class Meta: + model = User + fields = ( + "username", + "email", + "first_name", + "last_name", + "password", + "is_active", + "profile", + ) + extra_kwargs = { + "password": { + "style": {"input_type": "password"}, + "write_only": True, + }, + "first_name": { + "required": True, + "write_only": True, + }, + "last_name": { + "required": True, + "write_only": True, + }, + } + + profile = UserProfileSerializer(write_only=True) + is_active = rfs.BooleanField(default=False, read_only=True) + + def validate_profile(self, profile): + logger.info(f"{profile}") + + self._profile_serializer = UserProfileSerializer(data=profile) + self._profile_serializer.is_valid(raise_exception=True) + return profile + + def validate_password(self, password): + super().validate_password(password) + + if re.match(REGEX_PASSWORD, password): + return password + else: + raise ValidationError("Invalid password") + + def create(self, validated_data): + validated_data.pop("profile", None) + validated_data["is_active"] = False + user = None + try: + user = super().create(validated_data) + + # save profile object only if user object was actually saved + if getattr(user, "pk", None): + self._profile_serializer.save(user=user) + user.refresh_from_db() + except DatabaseError: + transaction.rollback() + return user + + +class EmailVerificationSerializer(rest_email_auth.serializers.EmailVerificationSerializer): + def validate_key(self, key): + try: + return super().validate_key(key) + except rfs.ValidationError as exc: + # custom error messages + err_str = str(exc.detail) + if "invalid" in err_str: + exc.detail = "The provided verification key" " is invalid or your email address is already verified." + if "expired" in err_str: + exc.detail = "The provided verification key" " has expired or your email address is already verified." + raise exc + + def save(self): + """ + Confirm the email address matching the confirmation key. + Then mark user as active. + """ + user = self._confirmation.email.user + with transaction.atomic(): + super().save() + + # Send msg on slack + if certego_apps_settings.SLACK_TOKEN and certego_apps_settings.DEFAULT_SLACK_CHANNEL: + try: + userprofile = user.user_profile + user_admin_link = f"{settings.HOST_URI}/admin/certego_saas_user/user/{user.pk}" + userprofile_admin_link = f"{settings.HOST_URI}" f"/admin/authentication/userprofile/{userprofile.pk}" + slack = Slack() + slack.send_message( + title="Newly registered user!!", + body=( + f"- User(#{user.pk}, {user.username}," + f"{user.email}, <{user_admin_link} |admin link>)\n" + f"- UserProfile({userprofile.company_name}," + f"{userprofile.company_role}, )" + f"<{userprofile_admin_link} |admin link>)" + ), + channel=certego_apps_settings.DEFAULT_SLACK_CHANNEL, + ) + except SlackApiError as exc: + slack.log.error(f"Slack message failed for user(#{user.pk}) with error: {str(exc)}") + + +class LoginSerializer(AuthTokenSerializer): + def validate(self, attrs): + try: + return super().validate(attrs) + except rfs.ValidationError as exc: + try: + user = User.objects.get(username=attrs["username"]) + except User.DoesNotExist: + # we do not want to leak info + # so just raise the original exception + raise exc + else: + # custom error messages + if not user.is_active: + if user.is_email_verified is False: + exc.detail = "Your account is pending email verification." + elif user.approved is None: + exc.detail = "Your account is pending activation by our team." + elif user.approved is False: + exc.detail = "Your account was declined." + logger.info(f"User {user} is not active. Error message: {exc.detail}") + # else + raise exc diff --git a/Submodules/GreedyBear/authentication/templates/authentication/emails/account-activated.html b/Submodules/GreedyBear/authentication/templates/authentication/emails/account-activated.html new file mode 100644 index 0000000..998b1f2 --- /dev/null +++ b/Submodules/GreedyBear/authentication/templates/authentication/emails/account-activated.html @@ -0,0 +1,18 @@ +{% extends "./base.html" %} + +{% block content %} +
+ Hello, {{ full_name }}! + +

+

You've got access! 🎉

+

+ +

+ Your account @{{ username }} has been verified by us and you + can now + login + to and use {{ host_name }}. +

+
+{% endblock %} \ No newline at end of file diff --git a/Submodules/GreedyBear/authentication/templates/authentication/emails/account-declined.html b/Submodules/GreedyBear/authentication/templates/authentication/emails/account-declined.html new file mode 100644 index 0000000..d0eb044 --- /dev/null +++ b/Submodules/GreedyBear/authentication/templates/authentication/emails/account-declined.html @@ -0,0 +1,13 @@ +{% extends "./base.html" %} {% block content %} +
+ Hello, {{ full_name }}! + +

+ We regret to inform you that your account request (@{{ username }}) on {{ host_name }} has been declined. You can sign up again with a + business email address and not a personal one to increase your chances of + getting access. +

+
+{% endblock %} diff --git a/Submodules/GreedyBear/authentication/templates/authentication/emails/base.html b/Submodules/GreedyBear/authentication/templates/authentication/emails/base.html new file mode 100644 index 0000000..265f522 --- /dev/null +++ b/Submodules/GreedyBear/authentication/templates/authentication/emails/base.html @@ -0,0 +1,27 @@ +{% load static %} + + +
+ GreedyBear: logo +
+
+ +
{% block content %} {% endblock %} +

+ Note: If you believe you received this email in error, please contact us at + {{ default_email }}. +

+
+ +
+
+ Thank you, +
+ GreedyBear © +
+ diff --git a/Submodules/GreedyBear/authentication/templates/authentication/emails/duplicate-email.html b/Submodules/GreedyBear/authentication/templates/authentication/emails/duplicate-email.html new file mode 100644 index 0000000..7238481 --- /dev/null +++ b/Submodules/GreedyBear/authentication/templates/authentication/emails/duplicate-email.html @@ -0,0 +1,21 @@ +{% load static %} + + +
+
+ Dear GreedyBear user, + +

+ As part of our commitment to keep GreedyBear and its users secure, we notify + you that someone just attempted to register with this email address. +

+
+
+ +
+
+ Thank you, +
+ GreedyBear © +
+ \ No newline at end of file diff --git a/Submodules/GreedyBear/authentication/templates/authentication/emails/reset-password.html b/Submodules/GreedyBear/authentication/templates/authentication/emails/reset-password.html new file mode 100644 index 0000000..22855a2 --- /dev/null +++ b/Submodules/GreedyBear/authentication/templates/authentication/emails/reset-password.html @@ -0,0 +1,36 @@ +{% load static %} + + +
+
+ Dear GreedyBear user, + +

+ Please click the link below to reset your password on GreedyBear. +

+ + + +

or, you may also copy and paste directly into your browser's URL bar.

+ +
{{ reset_url }}
+ +

+ Note: This URL is valid only for the next 24 hours. +

+ +

+ If you did not request a password reset, you can safely ignore this email. +

+
+
+ +
+
+ Thank you, +
+ GreedyBear © +
+ diff --git a/Submodules/GreedyBear/authentication/templates/authentication/emails/verify-email.html b/Submodules/GreedyBear/authentication/templates/authentication/emails/verify-email.html new file mode 100644 index 0000000..f4d3c34 --- /dev/null +++ b/Submodules/GreedyBear/authentication/templates/authentication/emails/verify-email.html @@ -0,0 +1,34 @@ +{% load static %} + + +
+
+ Hello! + +

+ Please click the link below to verify your email address. +

+ + + +

or, you may also copy and paste directly into your browser's URL bar.

+ +
{{ verification_url }}
+ +

+ Note: This URL is valid only for the next 24 hours. +

+ +
+
+ +
+
+ Thank you, +
+ GreedyBear © +
+ + diff --git a/Submodules/GreedyBear/authentication/urls.py b/Submodules/GreedyBear/authentication/urls.py new file mode 100644 index 0000000..3756394 --- /dev/null +++ b/Submodules/GreedyBear/authentication/urls.py @@ -0,0 +1,53 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +from .views import ( + APIAccessTokenView, + EmailVerificationView, + LoginView, + PasswordResetRequestView, + PasswordResetView, + RegistrationView, + ResendVerificationView, + TokenSessionsViewSet, + checkAuthentication, + checkConfiguration, +) + +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"sessions", TokenSessionsViewSet, basename="auth_tokensessions") + +urlpatterns = [ + # django-rest-email-auth + path( + "verify-email", + EmailVerificationView.as_view(), + name="auth_verify-email", + ), + path( + "resend-verification", + ResendVerificationView.as_view(), + name="auth_resend-verification", + ), + path( + "register", + RegistrationView.as_view(), + name="auth_register", + ), + path( + "request-password-reset", + PasswordResetRequestView.as_view(), + name="auth_request-password-reset", + ), + path("reset-password", PasswordResetView.as_view(), name="auth_reset-password"), + path("login", LoginView.as_view(), name="auth_login"), + path("configuration", checkConfiguration), + # auth + path("", include("certego_saas.apps.auth.urls")), + path("apiaccess", APIAccessTokenView.as_view(), name="auth_apiaccess"), + path("authentication", checkAuthentication), + path("", include(router.urls)), +] diff --git a/Submodules/GreedyBear/authentication/views.py b/Submodules/GreedyBear/authentication/views.py new file mode 100644 index 0000000..80d5e65 --- /dev/null +++ b/Submodules/GreedyBear/authentication/views.py @@ -0,0 +1,126 @@ +import logging +from typing import List + +import rest_email_auth.views +from certego_saas.apps.auth import views as certego_views +from certego_saas.apps.auth.backend import CookieTokenAuthentication +from certego_saas.ext.throttling import POSTUserRateThrottle +from django.conf import settings +from django.contrib.auth import get_user_model, login +from django.core.cache import cache +from durin import views as durin_views +from greedybear.consts import GET +from greedybear.enums import FrontendPage +from greedybear.settings import AUTH_USER_MODEL +from rest_framework import status +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response + +from .serializers import EmailVerificationSerializer, LoginSerializer, RegistrationSerializer + +logger = logging.getLogger(__name__) + +""" Auth API endpoints """ + +User: AUTH_USER_MODEL = get_user_model() + + +class PasswordResetRequestView(rest_email_auth.views.PasswordResetRequestView): + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + +class PasswordResetView(rest_email_auth.views.PasswordResetView): + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + +class EmailVerificationView(rest_email_auth.views.EmailVerificationView): + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + serializer_class = EmailVerificationSerializer + + +class RegistrationView(rest_email_auth.views.RegistrationView): + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + serializer_class = RegistrationSerializer + + +class ResendVerificationView(rest_email_auth.views.ResendVerificationView): + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + +@api_view([GET]) +@authentication_classes([CookieTokenAuthentication]) +@permission_classes([IsAuthenticated]) +def checkAuthentication(request): + logger.info(f"User: {request.user}, Administrator: {request.user.is_superuser}") + return Response({"is_superuser": request.user.is_superuser}, status=status.HTTP_200_OK) + + +@api_view([GET]) +def checkConfiguration(request): + logger.info(f"Requested checking configuration from {request.user}.") + page = request.query_params.get("page") + errors = {} + + if page == FrontendPage.REGISTER.value: + # email setup + if not settings.DEFAULT_FROM_EMAIL: + errors["DEFAULT_FROM_EMAIL"] = "required" + if not settings.DEFAULT_EMAIL: + errors["DEFAULT_EMAIL"] = "required" + + # if you are in production environment + if settings.STAGE_PRODUCTION: + # SES backend + if settings.AWS_SES: + if not settings.AWS_ACCESS_KEY_ID or not settings.AWS_SECRET_ACCESS_KEY: + errors["AWS SES backend"] = "configuration required" + else: + # SMTP backend + required_variables = [settings.EMAIL_HOST, settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD, settings.EMAIL_PORT] + for variable in required_variables: + if not variable: + errors["SMTP backend"] = "configuration required" + + logger.info(f"Configuration errors: {errors}") + if errors: + return Response(status=status.HTTP_501_NOT_IMPLEMENTED) + return Response(status=status.HTTP_200_OK) + + +class LoginView(certego_views.LoginView): + @staticmethod + def validate_and_return_user(request): + serializer = LoginSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + return serializer.validated_data["user"] + + def post(self, request, *args, **kwargs): + response = super().post(request, *args, **kwargs) + uname = request.user.username + logger.info(f"LoginView: received request from '{uname}'.") + if request.user.is_superuser: + try: + # pass admin user's session + login(request, request.user) + logger.info(f"administrator:'{uname}' was logged in.") + except Exception: + logger.exception(f"administrator:'{uname}' login failed.") + # just a hacky way to store the current host + # as this is the first endpoint hit by a user. + cache.set("current_site", request.get_host(), timeout=60 * 60 * 24) + return response + + +TokenSessionsViewSet = durin_views.TokenSessionsViewSet +APIAccessTokenView = durin_views.APIAccessTokenView diff --git a/Submodules/GreedyBear/configuration/nginx/django_server.conf b/Submodules/GreedyBear/configuration/nginx/django_server.conf new file mode 100644 index 0000000..35ee2a7 --- /dev/null +++ b/Submodules/GreedyBear/configuration/nginx/django_server.conf @@ -0,0 +1,30 @@ +server { + listen 80; + + server_name greedybear.com; + + server_tokens off; + charset utf-8; + + # Locations + include locations.conf; + + location /static/ { + alias /var/www/static/; + } + + # All requests to the Django/UWSGI server. + location / { + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Url-Scheme $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://uwsgi:8001; + client_max_body_size 100m; + } + + # Error pages + include errors.conf; + +} diff --git a/Submodules/GreedyBear/configuration/nginx/errors.conf b/Submodules/GreedyBear/configuration/nginx/errors.conf new file mode 100644 index 0000000..7ca2c20 --- /dev/null +++ b/Submodules/GreedyBear/configuration/nginx/errors.conf @@ -0,0 +1,59 @@ +error_page 500 /500.json; +location /500.json { + return 500 '{"code":500,"message":"Internal Server Error"}'; +} + +error_page 502 /502.json; +location /502.json { + return 502 '{"code":502,"message":"Bad Gateway"}'; +} + +error_page 503 /503.json; +location /503.json { + return 503 '{"code":503,"message":"Service Temporarily Unavailable"}'; +} + +error_page 504 /504.json; +location /504.json { + return 504 '{"code":504,"message":"Gateway Timeout"}'; +} + +error_page 400 /400.json; +location /400.json { + return 400 '{"code":400,"message":"Bad Request"}'; +} + +error_page 401 /401.json; +location /401.json { + return 401 '{"code":401,"message":"Unauthorized"}'; +} + +error_page 403 /403.json; +location /403.json { + return 403 '{"code":403,"message": "Forbidden"}'; +} + +error_page 404 /404.json; +location /404.json { + return 404 '{"code":404,"message":"Not Found"}'; +} + +error_page 408 /408.json; +location /408.json { + return 408 '{"code":408,"message":"Request Timeout}'; +} + +error_page 413 /413.json; +location /413.json { + return 413 '{"code":413,"message":"Request Entity Too Large"}'; +} + +error_page 418 /418.json; +location /418.json { + return 418 '{"code":418,"message":"I\'m a teapot"}'; +} + +error_page 429 /429.json; +location /429.json { + return 429 '{"code":429,"message":"Too Many Requests"}'; +} \ No newline at end of file diff --git a/Submodules/GreedyBear/configuration/nginx/http.conf b/Submodules/GreedyBear/configuration/nginx/http.conf new file mode 100644 index 0000000..74a3491 --- /dev/null +++ b/Submodules/GreedyBear/configuration/nginx/http.conf @@ -0,0 +1,64 @@ +# the upstream component nginx needs to connect to +upstream django { + server uwsgi:8001 fail_timeout=30s; +} + +uwsgi_cache_path /var/cache/nginx/feeds keys_zone=feeds_cache:10m max_size=10g + inactive=10m use_temp_path=off; +limit_req_zone $binary_remote_addr zone=adminlimit:10m rate=5r/m; + +server { + listen 80; + server_name localhost; + + server_tokens off; + + # Locations + include locations.conf; + + location /static/ { + alias /var/www/static/; + } + + location ^~/admin { + limit_req zone=adminlimit; + + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 45; + include uwsgi_params; + client_max_body_size 20m; + } + + location ~^/api/feeds { + limit_req zone=adminlimit; + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 600; + include uwsgi_params; + + gzip on; + gzip_types application/json; + gzip_min_length 1000; + + uwsgi_cache feeds_cache; + uwsgi_cache_key $scheme$host$uri$is_args$args; + uwsgi_cache_valid 200 10m; + add_header X-Cache-Status $upstream_cache_status; + } + + location / { + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 45; + include uwsgi_params; + client_max_body_size 20m; + } + + # Error pages + include errors.conf; + +} \ No newline at end of file diff --git a/Submodules/GreedyBear/configuration/nginx/https.conf b/Submodules/GreedyBear/configuration/nginx/https.conf new file mode 100644 index 0000000..1b78cf5 --- /dev/null +++ b/Submodules/GreedyBear/configuration/nginx/https.conf @@ -0,0 +1,76 @@ +# the upstream component nginx needs to connect to +upstream django { + server uwsgi:8001 fail_timeout=30s; +} + +uwsgi_cache_path /var/cache/nginx/feeds keys_zone=feeds_cache:10m max_size=10g + inactive=10m use_temp_path=off; + +server { + listen 80; + server_name greedybear.honeynet.com; + + include locations.conf; + + return 301 https://greedybear.honeynet.com$request_uri; +} + +server { + listen 443 ssl; + server_name greedybear.honeynet.com; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_certificate /usr/local/share/ca-certificates/greedybear.crt; + ssl_certificate_key /etc/ssl/private/greedybear.key; + ssl_password_file /etc/ssl/private/ssl_passwords.txt; + ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; + ssl_prefer_server_ciphers on; + + server_tokens off; + + # Locations + include locations.conf; + + location /static/ { + alias /var/www/static/; + } + + location ^~/admin { + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 45; + include uwsgi_params; + client_max_body_size 20m; + } + + location ~^/api/feeds { + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 600; + include uwsgi_params; + + gzip on; + gzip_types application/json; + gzip_min_length 1000; + + uwsgi_cache feeds_cache; + uwsgi_cache_key $scheme$host$uri$is_args$args; + uwsgi_cache_valid 200 10m; + add_header X-Cache-Status $upstream_cache_status; + } + + location / { + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 45; + include uwsgi_params; + client_max_body_size 20m; + } + + # Error pages + include errors.conf; + +} diff --git a/Submodules/GreedyBear/configuration/nginx/locations.conf b/Submodules/GreedyBear/configuration/nginx/locations.conf new file mode 100644 index 0000000..46cef19 --- /dev/null +++ b/Submodules/GreedyBear/configuration/nginx/locations.conf @@ -0,0 +1,8 @@ +location /hc { + return 200; +} + +location = /favicon.ico { + access_log off; + log_not_found off; +} \ No newline at end of file diff --git a/Submodules/GreedyBear/configuration/rabbitmq.conf b/Submodules/GreedyBear/configuration/rabbitmq.conf new file mode 100644 index 0000000..3249a2a --- /dev/null +++ b/Submodules/GreedyBear/configuration/rabbitmq.conf @@ -0,0 +1,3 @@ +# 150 hours, see https://www.rabbitmq.com/consumers.html#acknowledgement-timeout +# the time is long to avoid a lot of critical errors +consumer_timeout = 540000000 \ No newline at end of file diff --git a/Submodules/GreedyBear/configuration/uwsgi/greedybear.ini b/Submodules/GreedyBear/configuration/uwsgi/greedybear.ini new file mode 100644 index 0000000..0fd823e --- /dev/null +++ b/Submodules/GreedyBear/configuration/uwsgi/greedybear.ini @@ -0,0 +1,25 @@ +[uwsgi] +project = greedybear +base = /opt/deploy/greedybear + +chdir = %(base) +module = %(project).wsgi:application + +master = true +processes = 16 + +socket = 0.0.0.0:8001 +chown = www-data:www-data +vacuum = true +single-interpreter = true + +logto = /var/log/greedybear/uwsgi/greedybear.log +uid = www-data +gid = www-data + +max-requests = 1000 +max-worker-lifetime = 3600 +reload-on-rss = 2048 +worker-reload-mercy = 3600 + +buffer-size = 32768 diff --git a/Submodules/GreedyBear/docker/Dockerfile b/Submodules/GreedyBear/docker/Dockerfile new file mode 100644 index 0000000..427e04c --- /dev/null +++ b/Submodules/GreedyBear/docker/Dockerfile @@ -0,0 +1,50 @@ +# Stage 1: Frontend +FROM node:lts-alpine3.18 as frontend-build + +WORKDIR / +# copy react source code +COPY frontend/ . +# copy version file as an env file +COPY docker/.version .env.local +# install and build +RUN npm install npm@latest --location=global +RUN npm install + +RUN PUBLIC_URL=/static/reactapp/ npm run build + +# Stage 2: Backend +FROM python:3.11.9-alpine3.20 + +ENV PYTHONUNBUFFERED 1 +ENV DJANGO_SETTINGS_MODULE greedybear.settings +ENV PYTHONPATH /opt/deploy/greedybear +ENV LOG_PATH /var/log/greedybear + +ARG WATCHMAN=false + +RUN mkdir -p ${LOG_PATH} \ + ${LOG_PATH}/django \ + ${LOG_PATH}/uwsgi \ + # py3-psycopg2 is required to use PostgresSQL with Django + && apk --no-cache -U add bash py3-psycopg2 gcc python3-dev alpine-sdk linux-headers \ + && pip3 install --no-cache-dir --upgrade pip + +WORKDIR $PYTHONPATH +COPY . $PYTHONPATH +COPY --from=frontend-build /build /var/www/reactapp + +# separation is required to avoid to re-execute os installation in case of change of python requirements +RUN pip3 install --no-cache-dir --compile -r $PYTHONPATH/requirements/project-requirements.txt \ + && touch ${LOG_PATH}/django/api.log ${LOG_PATH}/django/api_errors.log \ + && touch ${LOG_PATH}/django/greedybear.log ${LOG_PATH}/django/greedybear_errors.log \ + && touch ${LOG_PATH}/django/celery.log ${LOG_PATH}/django/celery_errors.log \ + && touch ${LOG_PATH}/django/django_errors.log ${LOG_PATH}/django/elasticsearch.log\ + && touch ${LOG_PATH}/django/authentication.log ${LOG_PATH}/django/authentication_errors.log \ + && adduser -S -H -u 2000 -D -g www-data www-data \ + && chown -R www-data:www-data ${LOG_PATH} /opt/deploy/ \ + && rm -rf docs/ frontend/ tests/ .github/ \ + && /bin/bash ./docker/watchman_install.sh \ + && apk del gcc python3-dev alpine-sdk linux-headers + +HEALTHCHECK --interval=10s --timeout=2s --start-period=20s --retries=5 CMD nc -z localhost 8001 || exit 1 + diff --git a/Submodules/GreedyBear/docker/Dockerfile_nginx b/Submodules/GreedyBear/docker/Dockerfile_nginx new file mode 100644 index 0000000..f289d93 --- /dev/null +++ b/Submodules/GreedyBear/docker/Dockerfile_nginx @@ -0,0 +1,11 @@ +FROM library/nginx:1.27.0-alpine +RUN mkdir -p /var/cache/nginx /var/cache/nginx/feeds +RUN apk update && apk upgrade && apk add bash +ENV NGINX_LOG_DIR /var/log/nginx +# this is to avoid having these logs redirected to stdout/stderr +RUN rm $NGINX_LOG_DIR/access.log $NGINX_LOG_DIR/error.log +RUN touch $NGINX_LOG_DIR/access.log $NGINX_LOG_DIR/error.log +RUN chown 33:33 $NGINX_LOG_DIR/access.log $NGINX_LOG_DIR/error.log +VOLUME $NGINX_LOG_DIR + +HEALTHCHECK --interval=3s --start-period=2s --timeout=2s --retries=5 CMD curl --fail http://localhost/hc || exit 1 \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/default.yml b/Submodules/GreedyBear/docker/default.yml new file mode 100644 index 0000000..2512920 --- /dev/null +++ b/Submodules/GreedyBear/docker/default.yml @@ -0,0 +1,94 @@ +version: '3.8' + +x-no-healthcheck: &no-healthcheck + healthcheck: + disable: true + +services: + postgres: + image: library/postgres:13-alpine + container_name: greedybear_postgres + volumes: + - postgres_data:/var/lib/postgresql/data/ + env_file: + - ./env_file_postgres + + uwsgi: + image: intelowlproject/greedybear:prod + container_name: greedybear_uwsgi + volumes: + - ../configuration/uwsgi/greedybear.ini:/etc/uwsgi/sites/greedybear.ini + - generic_logs:/var/log/greedybear + - static_content:/opt/deploy/greedybear/static + entrypoint: + - ./docker/entrypoint_uwsgi.sh + expose: + - "8001" + - "1717" + env_file: + - env_file + depends_on: + - postgres + + nginx: + image: intelowlproject/greedybear_nginx:prod + container_name: greedybear_nginx + restart: unless-stopped + volumes: + - ../configuration/nginx/http.conf:/etc/nginx/conf.d/default.conf + - ../configuration/nginx/errors.conf:/etc/nginx/errors.conf + - ../configuration/nginx/locations.conf:/etc/nginx/locations.conf + - nginx_logs:/var/log/nginx + - static_content:/var/www/static + ports: + - "80:80" + depends_on: + - uwsgi + + rabbitmq: + image: library/rabbitmq:3.12-alpine + container_name: greedybear_rabbitmq + volumes: + - ../configuration/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf + logging: + driver: none + depends_on: + - postgres + + celery_beat: + image: intelowlproject/greedybear:prod + container_name: greedybear_celery_beat + restart: unless-stopped + command: /usr/local/bin/celery -A greedybear.celery beat --uid www-data --gid www-data --pidfile= --schedule=/tmp/celerybeat-schedule + volumes: + - generic_logs:/var/log/greedybear + env_file: + - env_file + depends_on: + - rabbitmq + - postgres + - uwsgi + <<: *no-healthcheck + + celery_worker_default: + image: intelowlproject/greedybear:prod + container_name: greedybear_celery_worker_default + restart: unless-stopped + stop_grace_period: 3m + command: /usr/local/bin/celery -A greedybear.celery worker -n worker_default --uid www-data --gid www-data --time-limit=10000 --pidfile= -Ofair -Q default -E -c 1 + volumes: + - generic_logs:/var/log/greedybear + env_file: + - env_file + depends_on: + - rabbitmq + - postgres + - uwsgi + <<: *no-healthcheck + + +volumes: + postgres_data: + nginx_logs: + generic_logs: + static_content: diff --git a/Submodules/GreedyBear/docker/elasticsearch.yml b/Submodules/GreedyBear/docker/elasticsearch.yml new file mode 100644 index 0000000..7e8fc5b --- /dev/null +++ b/Submodules/GreedyBear/docker/elasticsearch.yml @@ -0,0 +1,21 @@ +version: '3.8' + +services: + uwsgi: + depends_on: + - elasticsearch + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0 + environment: + - "discovery.type=single-node" + + kibana: + image: docker.elastic.co/kibana/kibana:7.17.0 + environment: + ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]' + ports: + - '5601:5601' + depends_on: + - elasticsearch + diff --git a/Submodules/GreedyBear/docker/entrypoint_uwsgi.sh b/Submodules/GreedyBear/docker/entrypoint_uwsgi.sh new file mode 100644 index 0000000..ed995ca --- /dev/null +++ b/Submodules/GreedyBear/docker/entrypoint_uwsgi.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +date +echo "starting wait_for_it for uwsgi" +/opt/deploy/greedybear/docker/wait-for-it.sh -t 15 postgres:$DB_PORT +date + +until cd /opt/deploy/greedybear +do + echo "Waiting for server volume..." +done + +# Apply database migrations +echo "Waiting for db to be ready..." +python manage.py makemigrations durin +python manage.py migrate + +# Collect static files +python manage.py collectstatic --noinput + +echo "------------------------------" +echo "DEBUG: " $DEBUG +echo "DJANGO_TEST_SERVER: " $DJANGO_TEST_SERVER +echo "------------------------------" + +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + python manage.py runserver 0.0.0.0:8001 +else + /usr/local/bin/uwsgi --ini /etc/uwsgi/sites/greedybear.ini --stats 127.0.0.1:1717 --stats-http +fi diff --git a/Submodules/GreedyBear/docker/env_file_postgres_template b/Submodules/GreedyBear/docker/env_file_postgres_template new file mode 100644 index 0000000..f373f5c --- /dev/null +++ b/Submodules/GreedyBear/docker/env_file_postgres_template @@ -0,0 +1,3 @@ +POSTGRES_PASSWORD=password +POSTGRES_USER=user +POSTGRES_DB=greedybear_db \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/env_file_template b/Submodules/GreedyBear/docker/env_file_template new file mode 100644 index 0000000..7fe51ce --- /dev/null +++ b/Submodules/GreedyBear/docker/env_file_template @@ -0,0 +1,43 @@ +# Required Secrets +ENVIRONMENT=production +DJANGO_SECRET= +DB_HOST=postgres +DB_PORT=5432 +DB_USER=user +DB_PASSWORD=password + +# used for automated correspondence from the site manager +DEFAULT_FROM_EMAIL= +# used for correspondence with users +DEFAULT_EMAIL= + +# SMTP backend +EMAIL_HOST= +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +EMAIL_PORT= +EMAIL_USE_TLS=False +EMAIL_USE_SSL=False + +# AWS +## S3 storage +AWS_IAM_ACCESS=False +### to use if no IAM credentials are provided +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +## region +AWS_REGION=eu-central-1 +## to use for sending mail with SES +AWS_SES=False + +ELASTIC_ENDPOINT= + +SLACK_TOKEN= +DEFAULT_SLACK_CHANNEL= + +STAGE="production" +DEBUG=False +MOCK_CONNECTIONS=False + +# True for public deployment, False for internal deployment +PUBLIC_DEPLOYMENT=False diff --git a/Submodules/GreedyBear/docker/https.override.yml b/Submodules/GreedyBear/docker/https.override.yml new file mode 100644 index 0000000..931d5c1 --- /dev/null +++ b/Submodules/GreedyBear/docker/https.override.yml @@ -0,0 +1,10 @@ +version: '3.8' + +services: + nginx: + volumes: + - ../configuration/nginx/https.conf:/etc/nginx/conf.d/default.conf + - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates + - /etc/ssl/private:/etc/ssl/private + ports: + - "443:443" \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/local.override.yml b/Submodules/GreedyBear/docker/local.override.yml new file mode 100644 index 0000000..4539d7a --- /dev/null +++ b/Submodules/GreedyBear/docker/local.override.yml @@ -0,0 +1,34 @@ +version: '3.8' + +services: + uwsgi: + build: + context: .. + dockerfile: docker/Dockerfile + args: + WATCHMAN: "true" + image: intelowlproject/greedybear:test + volumes: + - ../:/opt/deploy/greedybear + environment: + - DEBUG=True + - DJANGO_TEST_SERVER=True + - DJANGO_WATCHMAN_TIMEOUT=20 + + nginx: + build: + context: .. + dockerfile: docker/Dockerfile_nginx + image: intelowlproject/greedybear_nginx:test + volumes: + - ../configuration/nginx/django_server.conf:/etc/nginx/conf.d/default.conf + + celery_beat: + image: intelowlproject/greedybear:test + volumes: + - ../:/opt/deploy/greedybear + + celery_worker_default: + image: intelowlproject/greedybear:test + volumes: + - ../:/opt/deploy/greedybear \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/logrotate/application b/Submodules/GreedyBear/docker/logrotate/application new file mode 100644 index 0000000..486d694 --- /dev/null +++ b/Submodules/GreedyBear/docker/logrotate/application @@ -0,0 +1,10 @@ +/var/lib/docker/volumes/*_generic_logs/_data/*/*.log { + daily + missingok + rotate 2 + compress + delaycompress + notifempty + create 0644 www-data www-data + copytruncate +} \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/logrotate/docker b/Submodules/GreedyBear/docker/logrotate/docker new file mode 100644 index 0000000..cd0a9b9 --- /dev/null +++ b/Submodules/GreedyBear/docker/logrotate/docker @@ -0,0 +1,10 @@ +/var/log/docker/*.log { + daily + missingok + rotate 2 + compress + delaycompress + notifempty + create 0655 syslog adm + copytruncate +} diff --git a/Submodules/GreedyBear/docker/logrotate/nginx b/Submodules/GreedyBear/docker/logrotate/nginx new file mode 100644 index 0000000..47fccfb --- /dev/null +++ b/Submodules/GreedyBear/docker/logrotate/nginx @@ -0,0 +1,19 @@ +/var/lib/docker/volumes/*_nginx_logs/_data/*.log { + daily + missingok + rotate 8 + compress + delaycompress + notifempty + create 0644 www-data www-data + sharedscripts + prerotate + if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ + run-parts /etc/logrotate.d/httpd-prerotate; \ + fi \ + endscript + postrotate + container_id=$(docker ps -f name=greedybear_nginx --quiet) + docker exec $container_id killall -USR1 nginx + endscript +} \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/stag.override.yml b/Submodules/GreedyBear/docker/stag.override.yml new file mode 100644 index 0000000..d03e594 --- /dev/null +++ b/Submodules/GreedyBear/docker/stag.override.yml @@ -0,0 +1,14 @@ +version: '3.8' + +services: + uwsgi: + image: intelowlproject/greedybear:stag + + nginx: + image: intelowlproject/greedybear_nginx:stag + + celery_beat: + image: intelowlproject/greedybear:stag + + celery_worker_default: + image: intelowlproject/greedybear:stag \ No newline at end of file diff --git a/Submodules/GreedyBear/docker/wait-for-it.sh b/Submodules/GreedyBear/docker/wait-for-it.sh new file mode 100644 index 0000000..c7c1b72 --- /dev/null +++ b/Submodules/GreedyBear/docker/wait-for-it.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +# ref: https://github.com/vishnubob/wait-for-it +# docker problem: https://docs.docker.com/compose/startup-order/ + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi diff --git a/Submodules/GreedyBear/docker/watchman_install.sh b/Submodules/GreedyBear/docker/watchman_install.sh new file mode 100644 index 0000000..e9a6373 --- /dev/null +++ b/Submodules/GreedyBear/docker/watchman_install.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# This script can be disabled during development using REPO_DOWNLOADER_ENABLED=true env variable +if [ "$WATCHMAN" = "false" ]; then echo "Skipping WATCHMAN installation because we are not in test mode"; exit 0; fi + +apk add gcc linux-headers build-base +pip3 install --compile -r requirements/django-server-requirements.txt + +# install Watchman to enhance performance on the Django development Server +# https://docs.djangoproject.com/en/3.2/ref/django-admin/#runserver +cd /tmp +wget https://github.com/facebook/watchman/releases/download/v2024.04.15.00/watchman-v2024.04.15.00-linux.zip +unzip watchman-*-linux.zip +cd watchman-*-linux/ +mkdir -p /usr/local/{bin,lib} /usr/local/var/run/watchman +cp bin/* /usr/local/bin +cp lib/* /usr/local/lib +chmod 755 /usr/local/bin/watchman +chmod 2777 /usr/local/var/run/watchman +rm -rf watchman-*-linux* \ No newline at end of file diff --git a/Submodules/GreedyBear/frontend/babel.config.js b/Submodules/GreedyBear/frontend/babel.config.js new file mode 100644 index 0000000..48a4d79 --- /dev/null +++ b/Submodules/GreedyBear/frontend/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + "@babel/preset-env", + ["@babel/preset-react", { runtime: "automatic" }], + ], +}; diff --git a/Submodules/GreedyBear/frontend/index.html b/Submodules/GreedyBear/frontend/index.html new file mode 100644 index 0000000..5c86fab --- /dev/null +++ b/Submodules/GreedyBear/frontend/index.html @@ -0,0 +1,694 @@ + + + + + + + + + + + +GreedyBear - frontend - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ + + + + + +

GreedyBear - frontend

+

Built with @certego/certego-ui.

+

Design thesis

+
    +
  • Re-usable components/hooks/stores that other projects can also benefit from should be added to certego-ui package.
  • +
  • GreedyBear specific:
  • +
  • components should be added to src/components.
  • +
  • general hooks should be added to src/hooks.
  • +
  • zustand stores hooks should be added to src/stores.
  • +
+

Directory Structure

+
public/                                   public static assets
+|- icons/                                 icons/favicon
+|- index.html/                            root HTML file
+src/                                      source code
+|- components/                            pages and components
+|  |- auth/                               `certego_saas.apps.auth` (login, logout pages)
+|  |- dashboard/                          dashboard page and charts
+|  |- home/                               landing/home page
+|  |- Routes.jsx                          lazy route-component mappings
+|- constants/                             constant values
+|  |- api.js                              API URLs
+|  |- environment.js                      environment variables
+|  |- index.js                            GreedyBear specific constants
+|- hooks/                                 react hooks
+|- layouts/                               header, main, footer containers
+|- stores/                                zustand stores hooks
+|- styles/                                scss files
+|- wrappers/                              Higher-Order components
+|- App.jsx                                App component
+|- index.jsx                              Root JS file (ReactDOM renderer)
+
+

Local Development Environment

+

The frontend inside the docker containers does not hot-reload, so +you need to use CRA dev server on your host machine to serve pages when doing development on the frontend, using docker nginx only as API source.

+
    +
  • +

    Start GreedyBear containers (see docs). Original dockerized app is accessible on http://localhost:80

    +
  • +
  • +

    If you have not node-js installed, you have to do that. Follow the guide here. We tested this with NodeJS >=16.6

    +
  • +
  • +

    Install npm packages locally

    +
  • +
+
cd ./frontend && npm install
+
+
    +
  • Start CRA dev server:
  • +
+
npm start
+
+
    +
  • +

    Now you can access the auto-reloading frontend on http://localhost:3001. It acts as proxy for API requests to original app web server.

    +
  • +
  • +

    JS app main configs are available in package.json and enviroments.js.

    +
  • +
+

External Docs

+ +
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GreedyBear/frontend/jest.config.js b/Submodules/GreedyBear/frontend/jest.config.js new file mode 100644 index 0000000..f63766a --- /dev/null +++ b/Submodules/GreedyBear/frontend/jest.config.js @@ -0,0 +1,207 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/en/configuration.html + */ +const coverageReporters = [["text", { skipFull: true, skipEmpty: true }]]; +if (!process.env.STAGE_CI) { + // show HTML coverage only locally + coverageReporters.push(["html", { skipFull: true, skipEmpty: true }]); +} + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + collectCoverageFrom: ["src/**/*.js", "src/**/*.jsx"], + + // The directory where Jest should output its coverage files + coverageDirectory: ".coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: ["commons.js"], + + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: "babel", + + // A list of reporter names that Jest uses when writing coverage reports + // ["json", {skipFull: true, skipEmpty: true}], + // "lcov", + // "clover" + // eslint-disable-next-line object-shorthand + coverageReporters: coverageReporters, + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + globals: { + COOKIE: ["foo", "bar"], + }, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + moduleDirectories: ["node_modules"], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + moduleNameMapper: { + "\\.(css|scss)$": "/tests/styleMock", + "^certego(.*)$": "/src$1", + "^test-utils$": "/tests/testing-utils", + "^lodash-es$": "lodash", + "d3-(.*)$": "/node_modules/d3-$1/dist/d3-$1.min.js", + }, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + setupFilesAfterEnv: ["./tests/test-setup.js"], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "jsdom", + + // Options that will be passed to the testEnvironment + testEnvironmentOptions: { + url: "https://localhost", + }, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + /* node_modules can need to be transformated in this case we have to exclude them from the regex + Simply add node_modules name to the list + */ + transformIgnorePatterns: [ + `/node_modules/(?!(${[ + "axios", + "nanoid", + ].join("|")})/)`, + "\\.pnp\\.[^\\/]+$", + ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/Submodules/GreedyBear/frontend/package-lock.json b/Submodules/GreedyBear/frontend/package-lock.json new file mode 100644 index 0000000..1ca0ce1 --- /dev/null +++ b/Submodules/GreedyBear/frontend/package-lock.json @@ -0,0 +1,36137 @@ +{ + "name": "frontend", + "version": "0.1.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.1.0", + "dependencies": { + "@certego/certego-ui": "^0.1.10", + "axios": "^1.6.0", + "axios-hooks": "^3.0.4", + "bootstrap": ">=5.3.0", + "formik": "^2.2.9", + "prop-types": "^15.8.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-icons": "^4.9.0", + "react-router-dom": "^6.26.0", + "react-scripts": "^5.0.1", + "react-table": "^7.8.0", + "react-use": "^17.4.0", + "reactstrap": "^9.2.0", + "recharts": "^2.6.2", + "sass": "^1.72.0", + "zustand": "^4.5.2" + }, + "devDependencies": { + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.22.5", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^12.1.5", + "@testing-library/react-hooks": "^8.0.1", + "@testing-library/user-event": "^14.0.0", + "babel-eslint": "^10.1.0", + "babel-jest": "^29.7.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "prettier": "2.8.4", + "stylelint": "^16.8.1" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true + }, + "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.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "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/eslint-parser/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/@babel/eslint-parser/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/@babel/eslint-parser/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/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "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-define-polyfill-provider/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/highlight/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/@babel/parser": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "dependencies": { + "@babel/types": "^7.25.2" + }, + "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.23.3", + "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.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", + "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.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-decorators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.9.tgz", + "integrity": "sha512-KD7zDNaD14CRpjQjVbV4EnH9lsKYlcpUrhZH37ei2IY+AlXrfAPy5pTmRUE4X6X1k8EsKXPraykxeaogqQvSGA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.18.6" + }, + "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-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.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "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-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "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-decorators": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz", + "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==", + "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-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-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "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-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "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.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "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-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "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-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "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-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", + "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz", + "integrity": "sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.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.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-flow-strip-types": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz", + "integrity": "sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-flow": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", + "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "dependencies": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.9.tgz", + "integrity": "sha512-IrTYh1I3YCEL1trjknnlLKTp5JggjzhKl/d3ibzPc97JhpFcDTr38Jdek/oX4cFbS6By0bXJcOkpRvJ5ZHK2wQ==", + "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-react-display-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", + "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", + "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "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-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz", + "integrity": "sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.3.tgz", + "integrity": "sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q==", + "dependencies": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@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.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@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-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.3", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.3", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.3", + "@babel/plugin-transform-classes": "^7.23.3", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.3", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.3", + "@babel/plugin-transform-for-of": "^7.23.3", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.3", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.3", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.3", + "@babel/plugin-transform-numeric-separator": "^7.23.3", + "@babel/plugin-transform-object-rest-spread": "^7.23.3", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.3", + "@babel/plugin-transform-optional-chaining": "^7.23.3", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.3", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.3", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", + "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.3", + "core-js-compat": "^3.33.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.3" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", + "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-react-display-name": "^7.23.3", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", + "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "node_modules/@certego/certego-ui": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@certego/certego-ui/-/certego-ui-0.1.10.tgz", + "integrity": "sha512-53kLWdG56rMER1NztTOfjsesgQLTg0PPFHr/RKxvgjSU7xLZvbRHLTNR2Di0/baUsyPYg6iC8aZakSwtlUcwpA==", + "dependencies": { + "classnames": "^2.3.1", + "date-fns": "^2.28.0", + "match-sorter": "^6.3.1", + "nanoid": "^3.3.4", + "prop-types": "^15.8.1", + "react-compound-slider": "^3.3.1", + "react-icons": "^4.3.1", + "react-infinite-scroll-component": "^6.1.0", + "react-json-editor-ajrm": "^2.5.13", + "react-json-view": "^1.21.3", + "react-select": "^5.3.2", + "react-share": "^4.4.0", + "react-top-loading-bar": "^2.1.0", + "react-use": "^17.4.0", + "recharts": "^2.1.10", + "zustand": "^4.0.0-rc.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "axios": "^1.6.0", + "axios-hooks": "^3.0.4", + "bootstrap": "^5.2.1", + "formik": "^2.2.9", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-router-dom": "^6.3.0", + "react-table": "^7.8.0", + "reactstrap": "^9.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", + "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.1" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", + "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.1", + "@csstools/css-tokenizer": "^3.0.1" + } + }, + "node_modules/@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", + "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.9.2", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz", + "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==", + "dependencies": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/runtime": "^7.13.10", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^2.6.1", + "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/babel-plugin/node_modules/babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@emotion/babel-plugin/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/@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/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/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==", + "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/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/@eslint/eslintrc/node_modules/globals": { + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/load-nyc-config/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/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/console/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/core/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/@jest/core/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@jest/transform/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/@jest/transform/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "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.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/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/@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.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", + "integrity": "sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q==", + "dependencies": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.8.1", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <3.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", + "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@remix-run/router": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", + "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-node-resolve/node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "dependencies": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "dependencies": { + "@babel/types": "^7.12.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "dependencies": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@testing-library/dom": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.0.tgz", + "integrity": "sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz", + "integrity": "sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "@babel/runtime": "^7.9.2", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/react": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", + "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "<18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "<18.0.0", + "react-dom": "<18.0.0" + } + }, + "node_modules/@testing-library/react-hooks": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", + "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "react-error-boundary": "^3.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0", + "react": "^16.9.0 || ^17.0.0", + "react-dom": "^16.9.0 || ^17.0.0", + "react-test-renderer": "^16.9.0 || ^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-test-renderer": { + "optional": true + } + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.4.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", + "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz", + "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==", + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", + "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, + "node_modules/@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.29", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz", + "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "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==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "node_modules/@types/node": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", + "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==" + }, + "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/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" + }, + "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/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "17.0.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.53.tgz", + "integrity": "sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "17.0.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.18.tgz", + "integrity": "sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==", + "dev": true, + "dependencies": { + "@types/react": "^17" + } + }, + "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/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "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/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.7.tgz", + "integrity": "sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.30.7", + "@typescript-eslint/type-utils": "5.30.7", + "@typescript-eslint/utils": "5.30.7", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.30.7.tgz", + "integrity": "sha512-r218ZVL0zFBYzEq8/9K2ZhRgsmKUhm8xd3sWChgvTbmP98kHGuY83IUl64SS9fx9OSBM9vMLdzBfox4eDdm/ZQ==", + "dependencies": { + "@typescript-eslint/utils": "5.30.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.7.tgz", + "integrity": "sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.30.7", + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/typescript-estree": "5.30.7", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.7.tgz", + "integrity": "sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw==", + "dependencies": { + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/visitor-keys": "5.30.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.7.tgz", + "integrity": "sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==", + "dependencies": { + "@typescript-eslint/utils": "5.30.7", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.7.tgz", + "integrity": "sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.7.tgz", + "integrity": "sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==", + "dependencies": { + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/visitor-keys": "5.30.7", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.7.tgz", + "integrity": "sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.30.7", + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/typescript-estree": "5.30.7", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/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/@typescript-eslint/utils/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/@typescript-eslint/visitor-keys": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.7.tgz", + "integrity": "sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw==", + "dependencies": { + "@typescript-eslint/types": "5.30.7", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "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==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-globals/node_modules/acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "peerDependencies": { + "acorn": "^8" + } + }, + "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==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "dependencies": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz", + "integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "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-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "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==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "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": "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/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", + "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", + "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.20.3", + "caniuse-lite": "^1.0.30001335", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", + "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios-hooks": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/axios-hooks/-/axios-hooks-3.1.5.tgz", + "integrity": "sha512-mU4WZ9c6YiOTxgTIKbswoHvb/b9fWXa2FxNFKI/hVcfD9Qemz1r9KLfRSVZf1GZg8nFry7oTM5gxNmPSn5PG0Q==", + "dependencies": { + "@babel/runtime": "7.18.9", + "dequal": "2.0.3", + "lru-cache": "6.0.0" + }, + "peerDependencies": { + "axios": ">=0.24.0", + "react": "^16.8.0-0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/axios-hooks/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/axobject-query": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" + } + }, + "node_modules/babel-eslint/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "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==", + "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-loader/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==", + "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/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.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-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "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-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "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/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "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==" + }, + "node_modules/bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "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==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "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.11.0", + "raw-body": "2.5.2", + "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==", + "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==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.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==" + }, + "node_modules/bonjour-service": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.13.tgz", + "integrity": "sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/bootstrap": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.0.tgz", + "integrity": "sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.7" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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==" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "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/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camel-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/check-types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", + "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==" + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==" + }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, + "node_modules/clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/coa/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/coa/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/coa/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/coa/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/coa/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/coa/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/coa/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/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "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/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/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "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==", + "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==", + "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==" + }, + "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/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "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==", + "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==", + "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.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "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.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "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==" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.5.tgz", + "integrity": "sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.3.tgz", + "integrity": "sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==", + "dependencies": { + "browserslist": "^4.22.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.23.5.tgz", + "integrity": "sha512-8t78LdpKSuCq4pJYCYk8hl7XEkAX+BP16yRIwL3AanTksxuEf7CM83vRyctmiEL8NDZ3jpUcv56fk9/zG3aIuw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-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==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "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-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/create-jest/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.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": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", + "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-functions-list": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "dev": true, + "engines": { + "node": ">=12 || >=16" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "dependencies": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "node_modules/cssdb": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.6.3.tgz", + "integrity": "sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "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==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.12.tgz", + "integrity": "sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==", + "dependencies": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "dependencies": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "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/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "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-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-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "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-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "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/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-equal": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", + "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "dependencies": { + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.2", + "get-intrinsic": "^1.1.3", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-equal/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "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==" + }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "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/defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "dependencies": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + }, + "bin": { + "detective": "bin/detective.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "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==" + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "dev": true + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "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-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "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==" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.11.tgz", + "integrity": "sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dependencies": { + "has": "^1.0.3" + } + }, + "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==", + "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/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "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==" + }, + "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/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", + "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", + "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-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", + "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "dependencies": { + "@babel/runtime": "^7.20.7", + "aria-query": "^5.1.3", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.6.2", + "axobject-query": "^3.1.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.3", + "language-tags": "=1.0.5", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "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/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-testing-library": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.5.1.tgz", + "integrity": "sha512-plLEkkbAKBjPxsLj7x4jNapcHAg2ernkQlKKrN2I8NrQwPISZHyCUNvg5Hv3EDqOQReToQb5bnqXYbkijJPE/g==", + "dependencies": { + "@typescript-eslint/utils": "^5.13.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "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==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.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==", + "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-utils/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-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==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/eslint/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/eslint/node_modules/globals": { + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/eslint/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "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/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "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==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "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/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/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/expect/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.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.11.0", + "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==" + }, + "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==", + "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==" + }, + "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==", + "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/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-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==" + }, + "node_modules/fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.30" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "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==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "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==", + "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==", + "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==" + }, + "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==", + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + }, + "node_modules/flux": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", + "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formik": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz", + "integrity": "sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "dependencies": { + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^1.10.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "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==", + "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==", + "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==" + }, + "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==", + "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": "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/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "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-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "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/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==" + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "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-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==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "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==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "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==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/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==", + "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/hpack.js/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/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "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==" + }, + "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==", + "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==" + }, + "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==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": 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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "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.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", + "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", + "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "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==" + }, + "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-fresh/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/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/inline-style-prefixer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz", + "integrity": "sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ==", + "dependencies": { + "css-in-js-utils": "^2.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "engines": { + "node": ">= 10" + } + }, + "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==", + "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-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "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==", + "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==", + "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==", + "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-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-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/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "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-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "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==", + "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-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-changed-files/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-config/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-config/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-jsdom/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/jest-jasmine2/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/jest-jasmine2/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-mock/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-runner/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-runner/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-runtime/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-watcher/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "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": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "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==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", + "dependencies": { + "debug": "^2.1.3" + } + }, + "node_modules/jsonp/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/jsonp/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "dependencies": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/known-css-properties": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", + "dev": true + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "node_modules/language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "dependencies": { + "language-subtag-registry": "~0.3.2" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", + "engines": { + "node": ">=10" + } + }, + "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/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "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==", + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + }, + "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.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + }, + "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==" + }, + "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==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "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==" + }, + "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/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lower-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "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==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "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==", + "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==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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==" + }, + "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==" + }, + "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==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.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": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/nano-css": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.3.5.tgz", + "integrity": "sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==", + "dependencies": { + "css-tree": "^1.1.2", + "csstype": "^3.0.6", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^6.0.0", + "rtl-css-js": "^1.14.0", + "sourcemap-codec": "^1.4.8", + "stacktrace-js": "^2.0.2", + "stylis": "^4.0.6" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "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==", + "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==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/no-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "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": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + }, + "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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", + "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==" + }, + "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-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "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==", + "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==", + "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.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", + "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "dependencies": { + "array.prototype.reduce": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "dependencies": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "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==", + "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==", + "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/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "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/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/param-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "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-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/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/pascal-case/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "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": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "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/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "engines": { + "node": ">= 6" + } + }, + "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==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-dir/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/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "browserslist": ">=4", + "postcss": ">=8" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "dependencies": { + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", + "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "dependencies": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "dependencies": { + "postcss-selector-parser": "^6.0.6" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.1.10", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", + "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "dependencies": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "browserslist": ">= 4", + "postcss": ">= 8" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "dependencies": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz", + "integrity": "sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA==", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.0.4", + "@csstools/postcss-color-function": "^1.1.0", + "@csstools/postcss-font-format-keywords": "^1.0.0", + "@csstools/postcss-hwb-function": "^1.0.1", + "@csstools/postcss-ic-unit": "^1.0.0", + "@csstools/postcss-is-pseudo-class": "^2.0.6", + "@csstools/postcss-normalize-display-values": "^1.0.0", + "@csstools/postcss-oklab-function": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.1", + "@csstools/postcss-unset-value": "^1.0.1", + "autoprefixer": "^10.4.7", + "browserslist": "^4.21.0", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^6.6.3", + "postcss-attribute-case-insensitive": "^5.0.1", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.3", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.0", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.4", + "postcss-double-position-gradients": "^3.1.1", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.3", + "postcss-image-set-function": "^4.0.6", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.0", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.9", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.3", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.4", + "postcss-pseudo-class-any-link": "^7.1.5", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "dependencies": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", + "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "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==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/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/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==" + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "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/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==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/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==", + "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/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "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/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "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==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "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-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-app-polyfill/node_modules/promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "node_modules/react-compound-slider": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-3.4.0.tgz", + "integrity": "sha512-KSje/rB0xSvvcb7YV0+82hkiXTV5ljSS7axKrNiXLf9AEO+rrr1Xq4MJWA+6v030YNNo/RoSoEB6D6fnoy+8ng==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "d3-array": "^2.8.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.9" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.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-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "node_modules/react-icons": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", + "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-infinite-scroll-component": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", + "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", + "dependencies": { + "throttle-debounce": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "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/react-json-editor-ajrm": { + "version": "2.5.13", + "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.13.tgz", + "integrity": "sha512-uYRJFzY34w7coLxeWPFZGyQpWdBKK5e8R9jBZTJ5gAFp3WuGVG2DdGZ8oJKOVJy0hqkxS9DzJIzGmmxHHQ9afA==", + "dependencies": { + "@babel/runtime": "^7.0.0-rc.0" + }, + "peerDependencies": { + "react": ">=16.2.0", + "react-dom": ">=16.2.0" + } + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "dependencies": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/react-popper/node_modules/react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-router": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz", + "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==", + "dependencies": { + "@remix-run/router": "1.19.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz", + "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==", + "dependencies": { + "@remix-run/router": "1.19.1", + "react-router": "6.26.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "dependencies": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "node_modules/react-scripts/node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/react-scripts/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/react-scripts/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-scripts/node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/react-scripts/node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/react-scripts/node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/react-scripts/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/react-scripts/node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/react-scripts/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/react-scripts/node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "node_modules/react-scripts/node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "node_modules/react-scripts/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "deprecated": "Use your platform's native DOMException instead", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-scripts/node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-scripts/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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts/node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-watcher/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/react-scripts/node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/react-scripts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/react-scripts/node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/react-scripts/node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/react-scripts/node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/react-scripts/node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/react-scripts/node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/react-scripts/node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/react-scripts/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/react-scripts/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/react-select": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz", + "integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-share": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.0.tgz", + "integrity": "sha512-POe8Ge/JT9Ew9iyW7CiYsCCWCb8uMJWqFl9S7W0fJ/oH5gBJNzukH0bL5vSr17KKG5h15d3GfKaoviI22BKeYA==", + "dependencies": { + "classnames": "^2.2.5", + "jsonp": "^0.2.1" + }, + "engines": { + "node": ">=6.9.0", + "npm": ">=5.0.0" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17" + } + }, + "node_modules/react-smooth": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.3.tgz", + "integrity": "sha512-yl4y3XiMorss7ayF5QnBiSprig0+qFHui8uh7Hgg46QX5O+aRMRKlfGGNGLHno35JkQSvSYY8eCWkBfHfrSHfg==", + "dependencies": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-smooth/node_modules/dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/react-smooth/node_modules/react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "dependencies": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, + "node_modules/react-table": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17.0.0-0 || ^18.0.0" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz", + "integrity": "sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==", + "dependencies": { + "@babel/runtime": "^7.10.2", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-top-loading-bar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-top-loading-bar/-/react-top-loading-bar-2.1.0.tgz", + "integrity": "sha512-07IPCC4fThfkH5PHm7d49s9UAq2rpy4RAyD+5gtwtBbBrwxkvcff7ZlbCgzrBXq8AvcGafDkpjcGdntJ4F0O9A==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "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/react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "peerDependencies": { + "react": "*", + "tslib": "*" + } + }, + "node_modules/react-use": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.4.0.tgz", + "integrity": "sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==", + "dependencies": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.3.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-use/node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/react-use/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/reactstrap": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.0.tgz", + "integrity": "sha512-WWLTEG00qYav0E55PorWHReYTkz5IqkVmQNy0h6U81yqjSp9fOLFGV5pYSVeAUz+yRhU/RTE0oAWy22zr6sOIw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.6.0", + "classnames": "^2.2.3", + "prop-types": "^15.5.8", + "react-popper": "^2.2.4", + "react-transition-group": "^4.4.2" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "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/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.2.tgz", + "integrity": "sha512-dVhNfgI21LlF+4AesO3mj+i+9YdAAjoGaDWIctUgH/G2iy14YVtb/DSUeic77xr19rbKCiq+pQGfeg2kJQDHig==", + "dependencies": { + "classnames": "^2.2.5", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^8.0.4", + "react-smooth": "^2.0.2", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "prop-types": "^15.6.0", + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "dependencies": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, + "node_modules/reduce-css-calc/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==" + }, + "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.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "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==", + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "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/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "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": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "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/rollup": { + "version": "2.77.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz", + "integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/rtl-css-js": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.15.0.tgz", + "integrity": "sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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": { + "queue-microtask": "^1.2.2" + } + }, + "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-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "node_modules/sass": { + "version": "1.77.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", + "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", + "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": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "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": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==" + }, + "node_modules/selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "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==", + "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==" + }, + "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==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "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==", + "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==", + "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==", + "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==", + "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==" + }, + "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==" + }, + "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==" + }, + "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==", + "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==", + "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/set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==", + "engines": { + "node": ">=6.9" + } + }, + "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==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + }, + "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==", + "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==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "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==" + }, + "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/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "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==", + "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/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string_decoder/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==", + "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/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "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/string-width/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/string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "dependencies": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylelint": { + "version": "16.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.2.tgz", + "integrity": "sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "dependencies": { + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "@csstools/selector-specificity": "^4.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.2", + "css-tree": "^2.3.1", + "debug": "^4.3.6", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^9.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^5.3.2", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.34.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.7", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.1", + "postcss": "^8.4.41", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.0", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^7.1.0", + "supports-hyperlinks": "^3.0.0", + "svg-tags": "^1.0.0", + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint/node_modules/@csstools/selector-specificity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.1.0" + } + }, + "node_modules/stylelint/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/stylelint/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==", + "dev": true + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/stylelint/node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "dev": true, + "dependencies": { + "flat-cache": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/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==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/stylelint/node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/stylelint/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stylelint/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + } + }, + "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/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/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/svgo/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/svgo/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/svgo/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/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/svgo/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/svgo/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/svgo/node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/svgo/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/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tailwindcss": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.6.tgz", + "integrity": "sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg==", + "dependencies": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link/node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser/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/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" + }, + "node_modules/throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "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/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "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-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==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "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==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "node_modules/ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "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==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "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==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "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==", + "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/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.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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/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==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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-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.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "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==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/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==" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/victory-vendor": { + "version": "36.6.8", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.8.tgz", + "integrity": "sha512-H3kyQ+2zgjMPvbPqAl7Vwm2FD5dU7/4bCTQakFQnpIsfDljeOMDojRsrmJfwh4oAlNnWhpAf+mbAoLh8u7dwyQ==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-vendor/node_modules/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.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==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.73.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz", + "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz", + "integrity": "sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "node_modules/webpack/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/webpack/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/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "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==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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/whatwg-url/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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "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-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workbox-background-sync": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.3.tgz", + "integrity": "sha512-0DD/V05FAcek6tWv9XYj2w5T/plxhDSpclIcAGjA/b7t/6PdaRkQ7ZgtAX6Q/L7kV7wZ8uYRJUoH11VjNipMZw==", + "dependencies": { + "idb": "^6.1.4", + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.3.tgz", + "integrity": "sha512-4AwCIA5DiDrYhlN+Miv/fp5T3/whNmSL+KqhTwRBTZIL6pvTgE4lVuRzAt1JltmqyMcQ3SEfCdfxczuI4kwFQg==", + "dependencies": { + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-build": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.3.tgz", + "integrity": "sha512-8JNHHS7u13nhwIYCDea9MNXBNPHXCs5KDZPKI/ZNTr3f4sMGoD7hgFGecbyjX1gw4z6e9bMpMsOEJNyH5htA/w==", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.3", + "workbox-broadcast-update": "6.5.3", + "workbox-cacheable-response": "6.5.3", + "workbox-core": "6.5.3", + "workbox-expiration": "6.5.3", + "workbox-google-analytics": "6.5.3", + "workbox-navigation-preload": "6.5.3", + "workbox-precaching": "6.5.3", + "workbox-range-requests": "6.5.3", + "workbox-recipes": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3", + "workbox-streams": "6.5.3", + "workbox-sw": "6.5.3", + "workbox-window": "6.5.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.3.tgz", + "integrity": "sha512-6JE/Zm05hNasHzzAGKDkqqgYtZZL2H06ic2GxuRLStA4S/rHUfm2mnLFFXuHAaGR1XuuYyVCEey1M6H3PdZ7SQ==", + "dependencies": { + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-core": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.3.tgz", + "integrity": "sha512-Bb9ey5n/M9x+l3fBTlLpHt9ASTzgSGj6vxni7pY72ilB/Pb3XtN+cZ9yueboVhD5+9cNQrC9n/E1fSrqWsUz7Q==" + }, + "node_modules/workbox-expiration": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.3.tgz", + "integrity": "sha512-jzYopYR1zD04ZMdlbn/R2Ik6ixiXbi15c9iX5H8CTi6RPDz7uhvMLZPKEndZTpfgmUk8mdmT9Vx/AhbuCl5Sqw==", + "dependencies": { + "idb": "^6.1.4", + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-google-analytics": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.3.tgz", + "integrity": "sha512-3GLCHotz5umoRSb4aNQeTbILETcrTVEozSfLhHSBaegHs1PnqCmN0zbIy2TjTpph2AGXiNwDrWGF0AN+UgDNTw==", + "dependencies": { + "workbox-background-sync": "6.5.3", + "workbox-core": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.3.tgz", + "integrity": "sha512-bK1gDFTc5iu6lH3UQ07QVo+0ovErhRNGvJJO/1ngknT0UQ702nmOUhoN9qE5mhuQSrnK+cqu7O7xeaJ+Rd9Tmg==", + "dependencies": { + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-precaching": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.3.tgz", + "integrity": "sha512-sjNfgNLSsRX5zcc63H/ar/hCf+T19fRtTqvWh795gdpghWb5xsfEkecXEvZ8biEi1QD7X/ljtHphdaPvXDygMQ==", + "dependencies": { + "workbox-core": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3" + } + }, + "node_modules/workbox-range-requests": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.3.tgz", + "integrity": "sha512-pGCP80Bpn/0Q0MQsfETSfmtXsQcu3M2QCJwSFuJ6cDp8s2XmbUXkzbuQhCUzKR86ZH2Vex/VUjb2UaZBGamijA==", + "dependencies": { + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-recipes": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.3.tgz", + "integrity": "sha512-IcgiKYmbGiDvvf3PMSEtmwqxwfQ5zwI7OZPio3GWu4PfehA8jI8JHI3KZj+PCfRiUPZhjQHJ3v1HbNs+SiSkig==", + "dependencies": { + "workbox-cacheable-response": "6.5.3", + "workbox-core": "6.5.3", + "workbox-expiration": "6.5.3", + "workbox-precaching": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3" + } + }, + "node_modules/workbox-routing": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.3.tgz", + "integrity": "sha512-DFjxcuRAJjjt4T34RbMm3MCn+xnd36UT/2RfPRfa8VWJGItGJIn7tG+GwVTdHmvE54i/QmVTJepyAGWtoLPTmg==", + "dependencies": { + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-strategies": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.3.tgz", + "integrity": "sha512-MgmGRrDVXs7rtSCcetZgkSZyMpRGw8HqL2aguszOc3nUmzGZsT238z/NN9ZouCxSzDu3PQ3ZSKmovAacaIhu1w==", + "dependencies": { + "workbox-core": "6.5.3" + } + }, + "node_modules/workbox-streams": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.3.tgz", + "integrity": "sha512-vN4Qi8o+b7zj1FDVNZ+PlmAcy1sBoV7SC956uhqYvZ9Sg1fViSbOpydULOssVJ4tOyKRifH/eoi6h99d+sJ33w==", + "dependencies": { + "workbox-core": "6.5.3", + "workbox-routing": "6.5.3" + } + }, + "node_modules/workbox-sw": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.3.tgz", + "integrity": "sha512-BQBzm092w+NqdIEF2yhl32dERt9j9MDGUTa2Eaa+o3YKL4Qqw55W9yQC6f44FdAHdAJrJvp0t+HVrfh8AiGj8A==" + }, + "node_modules/workbox-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-Es8Xr02Gi6Kc3zaUwR691ZLy61hz3vhhs5GztcklQ7kl5k2qAusPh0s6LF3wEtlpfs9ZDErnmy5SErwoll7jBA==", + "dependencies": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.3" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.9.0" + } + }, + "node_modules/workbox-webpack-plugin/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==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/workbox-window": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.3.tgz", + "integrity": "sha512-GnJbx1kcKXDtoJBVZs/P7ddP0Yt52NNy4nocjBpYPiRhMqTpJCNrSL+fGHZ/i/oP6p/vhE8II0sA6AZGKGnssw==", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.3" + } + }, + "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/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": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "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/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "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/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "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": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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/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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.0.tgz", + "integrity": "sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==", + "dev": true + }, + "@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.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "requires": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + } + }, + "@babel/compat-data": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==" + }, + "@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@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" + }, + "dependencies": { + "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-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==" + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "requires": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", + "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "requires": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", + "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "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" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "requires": { + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "requires": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + } + }, + "@babel/helper-module-transforms": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "requires": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", + "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + } + }, + "@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "requires": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==" + }, + "@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==" + }, + "@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==" + }, + "@babel/helper-wrap-function": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", + "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "requires": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + } + }, + "@babel/helpers": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "requires": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" + } + }, + "@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "requires": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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" + } + } + } + }, + "@babel/parser": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "requires": { + "@babel/types": "^7.25.2" + } + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.23.3", + "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.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.23.3" + } + }, + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.3.tgz", + "integrity": "sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@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-decorators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.9.tgz", + "integrity": "sha512-KD7zDNaD14CRpjQjVbV4EnH9lsKYlcpUrhZH37ei2IY+AlXrfAPy5pTmRUE4X6X1k8EsKXPraykxeaogqQvSGA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.18.6" + } + }, + "@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-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.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "requires": {} + }, + "@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-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "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-decorators": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz", + "integrity": "sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@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-flow": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz", + "integrity": "sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@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-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-async-generator-functions": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.4.tgz", + "integrity": "sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", + "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "requires": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.20" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-static-block": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.3.tgz", + "integrity": "sha512-FGEQmugvAEu2QtgtU0uTASXevfLMFfBeVCIIdcQhn/uBQsMTjBajdnAtanQlOcuihWh10PZ7+HWvc7NtBwP74w==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.15" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dynamic-import": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-export-namespace-from": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.9.tgz", + "integrity": "sha512-+G6rp2zRuOAInY5wcggsx4+QVao1qPM0osC9fTUVlAV3zOrzTCnrMAFVnR6+a3T8wz1wFIH7KhYMcMB3u1n80A==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-flow": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", + "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-json-strings": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-logical-assignment-operators": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", + "requires": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "requires": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-transform-numeric-separator": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-transform-object-rest-spread": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", + "requires": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.23.3" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20" + } + }, + "@babel/plugin-transform-optional-catch-binding": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-transform-optional-chaining": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-methods": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-property-in-object": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.18.9.tgz", + "integrity": "sha512-IrTYh1I3YCEL1trjknnlLKTp5JggjzhKl/d3ibzPc97JhpFcDTr38Jdek/oX4cFbS6By0bXJcOkpRvJ5ZHK2wQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", + "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", + "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@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" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz", + "integrity": "sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-typescript": "^7.18.6" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-property-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-sets-regex": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/preset-env": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.3.tgz", + "integrity": "sha512-ovzGc2uuyNfNAs/jyjIGxS8arOHS5FENZaNn4rtE7UdKMMkqHCvboHfcuhWLZNX5cB44QfcGNWjaevxMzzMf+Q==", + "requires": { + "@babel/compat-data": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@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.23.3", + "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@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-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.23.3", + "@babel/plugin-transform-async-generator-functions": "^7.23.3", + "@babel/plugin-transform-async-to-generator": "^7.23.3", + "@babel/plugin-transform-block-scoped-functions": "^7.23.3", + "@babel/plugin-transform-block-scoping": "^7.23.3", + "@babel/plugin-transform-class-properties": "^7.23.3", + "@babel/plugin-transform-class-static-block": "^7.23.3", + "@babel/plugin-transform-classes": "^7.23.3", + "@babel/plugin-transform-computed-properties": "^7.23.3", + "@babel/plugin-transform-destructuring": "^7.23.3", + "@babel/plugin-transform-dotall-regex": "^7.23.3", + "@babel/plugin-transform-duplicate-keys": "^7.23.3", + "@babel/plugin-transform-dynamic-import": "^7.23.3", + "@babel/plugin-transform-exponentiation-operator": "^7.23.3", + "@babel/plugin-transform-export-namespace-from": "^7.23.3", + "@babel/plugin-transform-for-of": "^7.23.3", + "@babel/plugin-transform-function-name": "^7.23.3", + "@babel/plugin-transform-json-strings": "^7.23.3", + "@babel/plugin-transform-literals": "^7.23.3", + "@babel/plugin-transform-logical-assignment-operators": "^7.23.3", + "@babel/plugin-transform-member-expression-literals": "^7.23.3", + "@babel/plugin-transform-modules-amd": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-modules-systemjs": "^7.23.3", + "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.23.3", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.3", + "@babel/plugin-transform-numeric-separator": "^7.23.3", + "@babel/plugin-transform-object-rest-spread": "^7.23.3", + "@babel/plugin-transform-object-super": "^7.23.3", + "@babel/plugin-transform-optional-catch-binding": "^7.23.3", + "@babel/plugin-transform-optional-chaining": "^7.23.3", + "@babel/plugin-transform-parameters": "^7.23.3", + "@babel/plugin-transform-private-methods": "^7.23.3", + "@babel/plugin-transform-private-property-in-object": "^7.23.3", + "@babel/plugin-transform-property-literals": "^7.23.3", + "@babel/plugin-transform-regenerator": "^7.23.3", + "@babel/plugin-transform-reserved-words": "^7.23.3", + "@babel/plugin-transform-shorthand-properties": "^7.23.3", + "@babel/plugin-transform-spread": "^7.23.3", + "@babel/plugin-transform-sticky-regex": "^7.23.3", + "@babel/plugin-transform-template-literals": "^7.23.3", + "@babel/plugin-transform-typeof-symbol": "^7.23.3", + "@babel/plugin-transform-unicode-escapes": "^7.23.3", + "@babel/plugin-transform-unicode-property-regex": "^7.23.3", + "@babel/plugin-transform-unicode-regex": "^7.23.3", + "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "dependencies": { + "@babel/helper-define-polyfill-provider": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz", + "integrity": "sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", + "integrity": "sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==", + "requires": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.3", + "semver": "^6.3.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", + "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.4.3", + "core-js-compat": "^3.33.1" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz", + "integrity": "sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.4.3" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", + "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-react-display-name": "^7.23.3", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.23.3" + } + }, + "@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + } + }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "@babel/runtime": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.4.tgz", + "integrity": "sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==", + "requires": { + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } + } + }, + "@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "requires": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" + } + }, + "@babel/traverse": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "requires": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", + "debug": "^4.3.1", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "requires": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "@certego/certego-ui": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/@certego/certego-ui/-/certego-ui-0.1.10.tgz", + "integrity": "sha512-53kLWdG56rMER1NztTOfjsesgQLTg0PPFHr/RKxvgjSU7xLZvbRHLTNR2Di0/baUsyPYg6iC8aZakSwtlUcwpA==", + "requires": { + "classnames": "^2.3.1", + "date-fns": "^2.28.0", + "match-sorter": "^6.3.1", + "nanoid": "^3.3.4", + "prop-types": "^15.8.1", + "react-compound-slider": "^3.3.1", + "react-icons": "^4.3.1", + "react-infinite-scroll-component": "^6.1.0", + "react-json-editor-ajrm": "^2.5.13", + "react-json-view": "^1.21.3", + "react-select": "^5.3.2", + "react-share": "^4.4.0", + "react-top-loading-bar": "^2.1.0", + "react-use": "^17.4.0", + "recharts": "^2.1.10", + "zustand": "^4.0.0-rc.0" + } + }, + "@csstools/css-parser-algorithms": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.1.tgz", + "integrity": "sha512-lSquqZCHxDfuTg/Sk2hiS0mcSFCEBuj49JfzPHJogDBT0mGCyY5A1AQzBWngitrp7i1/HAZpIgzF/VjhOEIJIg==", + "dev": true, + "requires": {} + }, + "@csstools/css-tokenizer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.1.tgz", + "integrity": "sha512-UBqaiu7kU0lfvaP982/o3khfXccVlHPWp0/vwwiIgDF0GmqqqxoiXC/6FCjlS9u92f7CoEz6nXKQnrn1kIAkOw==", + "dev": true + }, + "@csstools/media-query-list-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-3.0.1.tgz", + "integrity": "sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==", + "dev": true, + "requires": {} + }, + "@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "@csstools/postcss-cascade-layers": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", + "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "requires": {} + }, + "@dual-bundle/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", + "dev": true + }, + "@emotion/babel-plugin": { + "version": "11.9.2", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz", + "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==", + "requires": { + "@babel/helper-module-imports": "^7.12.13", + "@babel/plugin-syntax-jsx": "^7.12.13", + "@babel/runtime": "^7.13.10", + "@emotion/hash": "^0.8.0", + "@emotion/memoize": "^0.7.5", + "@emotion/serialize": "^1.0.2", + "babel-plugin-macros": "^2.6.1", + "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" + }, + "dependencies": { + "babel-plugin-macros": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", + "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==", + "requires": { + "@babel/runtime": "^7.7.2", + "cosmiconfig": "^6.0.0", + "resolve": "^1.12.0" + } + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, + "@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/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/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==", + "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": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "globals": { + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "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" + } + }, + "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==" + } + } + }, + "@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==", + "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==" + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "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" + } + }, + "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" + } + }, + "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" + } + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" + }, + "@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + } + } + }, + "@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "requires": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + } + } + }, + "@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "requires": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + } + } + }, + "@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@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.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "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" + } + } + } + }, + "@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.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz", + "integrity": "sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q==", + "requires": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.8.1", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "@popperjs/core": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", + "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==" + }, + "@remix-run/router": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", + "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==" + }, + "@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, + "@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "dependencies": { + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + } + } + }, + "@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + } + } + }, + "@rushstack/eslint-patch": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz", + "integrity": "sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==" + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "requires": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + }, + "@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + } + }, + "@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "requires": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "requires": { + "@babel/types": "^7.12.6" + } + }, + "@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "requires": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + } + }, + "@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "requires": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "dependencies": { + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + } + } + }, + "@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + } + }, + "@testing-library/dom": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.16.0.tgz", + "integrity": "sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + } + }, + "@testing-library/jest-dom": { + "version": "6.4.8", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz", + "integrity": "sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.4.0", + "@babel/runtime": "^7.9.2", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "dependencies": { + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + } + } + }, + "@testing-library/react": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", + "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "<18.0.0" + } + }, + "@testing-library/react-hooks": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz", + "integrity": "sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "react-error-boundary": "^3.1.0" + } + }, + "@testing-library/user-event": { + "version": "14.4.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", + "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", + "dev": true, + "requires": {} + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz", + "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==", + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/d3-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.4.tgz", + "integrity": "sha512-nwvEkG9vYOc0Ic7G7kwgviY4AQlTfYGIZ0fqB7CQHXGyYM6nO7kJh5EguSNA3jfh4rq7Sb7eMVq8isuvg2/miQ==" + }, + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, + "@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==" + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.29", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz", + "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "requires": { + "@types/node": "*" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "@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==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "@types/node": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz", + "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw==" + }, + "@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/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" + }, + "@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/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/react": { + "version": "17.0.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.53.tgz", + "integrity": "sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.18.tgz", + "integrity": "sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==", + "dev": true, + "requires": { + "@types/react": "^17" + } + }, + "@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/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@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/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==" + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", + "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.7.tgz", + "integrity": "sha512-l4L6Do+tfeM2OK0GJsU7TUcM/1oN/N25xHm3Jb4z3OiDU4Lj8dIuxX9LpVMS9riSXQs42D1ieX7b85/r16H9Fw==", + "requires": { + "@typescript-eslint/scope-manager": "5.30.7", + "@typescript-eslint/type-utils": "5.30.7", + "@typescript-eslint/utils": "5.30.7", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.30.7.tgz", + "integrity": "sha512-r218ZVL0zFBYzEq8/9K2ZhRgsmKUhm8xd3sWChgvTbmP98kHGuY83IUl64SS9fx9OSBM9vMLdzBfox4eDdm/ZQ==", + "requires": { + "@typescript-eslint/utils": "5.30.7" + } + }, + "@typescript-eslint/parser": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.7.tgz", + "integrity": "sha512-Rg5xwznHWWSy7v2o0cdho6n+xLhK2gntImp0rJroVVFkcYFYQ8C8UJTSuTw/3CnExBmPjycjmUJkxVmjXsld6A==", + "requires": { + "@typescript-eslint/scope-manager": "5.30.7", + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/typescript-estree": "5.30.7", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.7.tgz", + "integrity": "sha512-7BM1bwvdF1UUvt+b9smhqdc/eniOnCKxQT/kj3oXtj3LqnTWCAM0qHRHfyzCzhEfWX0zrW7KqXXeE4DlchZBKw==", + "requires": { + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/visitor-keys": "5.30.7" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.7.tgz", + "integrity": "sha512-nD5qAE2aJX/YLyKMvOU5jvJyku4QN5XBVsoTynFrjQZaDgDV6i7QHFiYCx10wvn7hFvfuqIRNBtsgaLe0DbWhw==", + "requires": { + "@typescript-eslint/utils": "5.30.7", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.7.tgz", + "integrity": "sha512-ocVkETUs82+U+HowkovV6uxf1AnVRKCmDRNUBUUo46/5SQv1owC/EBFkiu4MOHeZqhKz2ktZ3kvJJ1uFqQ8QPg==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.7.tgz", + "integrity": "sha512-tNslqXI1ZdmXXrHER83TJ8OTYl4epUzJC0aj2i4DMDT4iU+UqLT3EJeGQvJ17BMbm31x5scSwo3hPM0nqQ1AEA==", + "requires": { + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/visitor-keys": "5.30.7", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.7.tgz", + "integrity": "sha512-Z3pHdbFw+ftZiGUnm1GZhkJgVqsDL5CYW2yj+TB2mfXDFOMqtbzQi2dNJIyPqPbx9mv2kUxS1gU+r2gKlKi1rQ==", + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.30.7", + "@typescript-eslint/types": "5.30.7", + "@typescript-eslint/typescript-estree": "5.30.7", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "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" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.7.tgz", + "integrity": "sha512-KrRXf8nnjvcpxDFOKej4xkD7657+PClJs5cJVSG7NNoCNnjEdc46juNAQt7AyuWctuCgs6mVRc1xGctEqrjxWw==", + "requires": { + "@typescript-eslint/types": "5.30.7", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==" + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "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==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==" + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + }, + "dependencies": { + "acorn-walk": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "dev": true, + "requires": { + "acorn": "^8.11.0" + } + } + } + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} + }, + "acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", + "requires": { + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, + "address": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.0.tgz", + "integrity": "sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==" + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "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-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + }, + "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": "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" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "requires": { + "deep-equal": "^2.0.5" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.reduce": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", + "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "array.prototype.tosorted": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", + "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + } + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==" + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "autoprefixer": { + "version": "10.4.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", + "integrity": "sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==", + "requires": { + "browserslist": "^4.20.3", + "caniuse-lite": "^1.0.30001335", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "axe-core": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.6.3.tgz", + "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==" + }, + "axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "axios-hooks": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/axios-hooks/-/axios-hooks-3.1.5.tgz", + "integrity": "sha512-mU4WZ9c6YiOTxgTIKbswoHvb/b9fWXa2FxNFKI/hVcfD9Qemz1r9KLfRSVZf1GZg8nFry7oTM5gxNmPSn5PG0Q==", + "requires": { + "@babel/runtime": "7.18.9", + "dequal": "2.0.3", + "lru-cache": "6.0.0" + }, + "dependencies": { + "@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" + } + } + } + }, + "axobject-query": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "requires": { + "deep-equal": "^2.0.5" + } + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "requires": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "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-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "requires": {} + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz", + "integrity": "sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.1", + "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-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "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==" + }, + "bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "requires": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "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.11.0", + "raw-body": "2.5.2", + "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==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.13.tgz", + "integrity": "sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA==", + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "bootstrap": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.0.tgz", + "integrity": "sha512-UnBV3E3v4STVNQdms6jSGO2CvOkjUMdDAVR2V5N4uCMdaIkaQjbcEAMqRimDHIs4uqBYzDAKCQwCB+97tJgHQw==", + "requires": {} + }, + "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.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "requires": { + "fill-range": "^7.1.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "requires": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "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==" + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "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==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==" + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + }, + "check-types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz", + "integrity": "sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==" + }, + "cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==" + }, + "classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + }, + "clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "requires": { + "source-map": "~0.6.0" + } + }, + "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==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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==" + }, + "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==" + }, + "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==" + }, + "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" + } + } + } + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==" + }, + "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==" + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "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==", + "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==", + "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==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, + "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==", + "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==" + } + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "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.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.5.tgz", + "integrity": "sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg==" + }, + "core-js-compat": { + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.3.tgz", + "integrity": "sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==", + "requires": { + "browserslist": "^4.22.1" + } + }, + "core-js-pure": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.23.5.tgz", + "integrity": "sha512-8t78LdpKSuCq4pJYCYk8hl7XEkAX+BP16yRIwL3AanTksxuEf7CM83vRyctmiEL8NDZ3jpUcv56fk9/zG3aIuw==" + }, + "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==" + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "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-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "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": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-declaration-sorter": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", + "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "requires": {} + }, + "css-functions-list": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "dev": true + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-in-js-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", + "requires": { + "hyphenate-style-name": "^1.0.2", + "isobject": "^3.0.1" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "cssdb": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-6.6.3.tgz", + "integrity": "sha512-7GDvDSmE+20+WcSMhP17Q1EVWUrLlbxxpMDqG731n8P99JhnQZHR9YvtjPvEHfjFUjvQJvdpKCjlKOX+xe4UVA==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.12.tgz", + "integrity": "sha512-TgvArbEZu0lk/dvg2ja+B7kYoD7BBCmn3+k58xD0qjrGHsFzXY/wKTo9M5egcUCabPol05e/PVoIu79s2JN4WQ==", + "requires": { + "cssnano-preset-default": "^5.2.12", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", + "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "requires": { + "css-declaration-sorter": "^6.3.0", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.0", + "postcss-convert-values": "^5.1.2", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.6", + "postcss-merge-rules": "^5.1.2", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.3", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "csstype": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", + "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==" + }, + "d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } + }, + "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-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-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "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-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "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==" + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "date-fns": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", + "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==" + }, + "debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "requires": {} + }, + "deep-equal": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.0.tgz", + "integrity": "sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==", + "requires": { + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.2", + "get-intrinsic": "^1.1.3", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, + "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==" + }, + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "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" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==" + } + } + }, + "detective": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz", + "integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==", + "requires": { + "acorn-node": "^1.8.2", + "defined": "^1.0.0", + "minimist": "^1.2.6" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "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==" + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-accessibility-api": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", + "integrity": "sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==", + "dev": true + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "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-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "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==" + }, + "ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.5.11", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.11.tgz", + "integrity": "sha512-R1CccCDYqndR25CaXFd6hp/u9RaaMcftMkphmvuepXr5b1vfLkRml6aWVeBhXJ7rbevHkKEMJtz8XqPf7ffmew==" + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "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" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + } + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==" + }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + } + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "requires": { + "has": "^1.0.3" + } + }, + "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==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==" + }, + "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==" + }, + "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==" + }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz", + "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==", + "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": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "globals": { + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "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" + } + }, + "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==" + } + } + }, + "eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + } + }, + "eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "requires": {} + }, + "eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "requires": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "requires": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", + "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "requires": { + "@babel/runtime": "^7.20.7", + "aria-query": "^5.1.3", + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "ast-types-flow": "^0.0.7", + "axe-core": "^4.6.2", + "axobject-query": "^3.1.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "has": "^1.0.3", + "jsx-ast-utils": "^3.3.3", + "language-tags": "=1.0.5", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "eslint-plugin-react": { + "version": "7.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", + "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.0", + "string.prototype.matchall": "^4.0.8" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "requires": {} + }, + "eslint-plugin-testing-library": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.5.1.tgz", + "integrity": "sha512-plLEkkbAKBjPxsLj7x4jNapcHAg2ernkQlKKrN2I8NrQwPISZHyCUNvg5Hv3EDqOQReToQb5bnqXYbkijJPE/g==", + "requires": { + "@typescript-eslint/utils": "^5.13.0" + } + }, + "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==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "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==" + } + } + }, + "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==" + }, + "eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "requires": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "requires": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "requires": { + "estraverse": "^5.1.0" + } + }, + "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" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "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==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.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.11.0", + "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==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==" + }, + "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==" + } + } + }, + "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-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==" + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "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==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "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==" + }, + "fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true + }, + "fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "requires": { + "bser": "2.1.1" + } + }, + "fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "requires": { + "fbjs": "^3.0.0" + } + }, + "fbjs": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.4.tgz", + "integrity": "sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==", + "requires": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.30" + } + }, + "fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "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==", + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "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==", + "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==", + "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==" + } + } + }, + "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==", + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.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==", + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + }, + "flux": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.3.tgz", + "integrity": "sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==", + "requires": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + } + }, + "follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "formik": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.2.9.tgz", + "integrity": "sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==", + "requires": { + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^1.10.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==" + }, + "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==", + "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==", + "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==" + }, + "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==" + }, + "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": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "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==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "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==" + }, + "harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "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-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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "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==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "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" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "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==", + "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" + } + }, + "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" + } + } + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true + }, + "html-webpack-plugin": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==", + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "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==", + "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==" + }, + "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==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "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" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "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.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} + }, + "idb": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/idb/-/idb-6.1.5.tgz", + "integrity": "sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==" + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" + }, + "immer": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.15.tgz", + "integrity": "sha512-2eB/sswms9AEUSkOm4SbV5Y7Vmt/bKRwByd52jfLkW4OLYeaTP3EEiJ9agqU0O/tq6Dk62Zfj+TJSqfm1rLVGQ==" + }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==" + }, + "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" + }, + "dependencies": { + "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==" + } + } + }, + "import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "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==" + }, + "inline-style-prefixer": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz", + "integrity": "sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ==", + "requires": { + "css-in-js-utils": "^2.0.0" + } + }, + "internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + } + }, + "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==", + "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==", + "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==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "requires": { + "has": "^1.0.3" + } + }, + "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==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "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==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "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-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "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==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "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==", + "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==" + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + }, + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "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==" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" + }, + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "requires": { + "semver": "^7.5.3" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + } + }, + "jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + } + } + }, + "jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "dependencies": { + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } + } + }, + "jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "requires": {} + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==" + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "requires": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "dependencies": { + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + } + } + }, + "jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + } + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "dependencies": { + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + } + } + }, + "jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "dependencies": { + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + } + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "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": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "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": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "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==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", + "requires": { + "debug": "^2.1.3" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==" + } + } + }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, + "jsx-ast-utils": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "requires": { + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" + } + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "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==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==" + }, + "known-css-properties": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.34.0.tgz", + "integrity": "sha512-tBECoUqNFbyAY4RrbqsBQqDFpGXAEbdD5QKr8kACx3+rnArmuuR22nKQWKazvp07N9yjTyDZaw/20UIH8tL9DQ==", + "dev": true + }, + "language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==" + }, + "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==" + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + }, + "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==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + }, + "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.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + }, + "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==" + }, + "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==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "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" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==", + "dev": true + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "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==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "requires": { + "tmpl": "1.0.5" + } + }, + "match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "requires": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "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==" + }, + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, + "meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "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==" + }, + "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==" + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "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": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "nano-css": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.3.5.tgz", + "integrity": "sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==", + "requires": { + "css-tree": "^1.1.2", + "csstype": "^3.0.6", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^6.0.0", + "rtl-css-js": "^1.14.0", + "sourcemap-codec": "^1.4.8", + "stacktrace-js": "^2.0.2", + "stylis": "^4.0.6" + } + }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "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==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "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": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "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==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "nwsapi": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz", + "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==" + }, + "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-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "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.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.fromentries": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", + "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "requires": { + "array.prototype.reduce": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.1" + } + }, + "object.hasown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "requires": { + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "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==", + "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==" + }, + "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" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "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" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "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==" + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "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-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" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "requires": { + "entities": "^4.4.0" + }, + "dependencies": { + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + }, + "dependencies": { + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "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": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==" + }, + "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==", + "requires": { + "find-up": "^4.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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" + } + } + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.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==" + } + } + }, + "postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "requires": {} + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", + "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", + "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "requires": { + "browserslist": "^4.20.3", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-properties": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", + "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "requires": {} + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "requires": {} + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "requires": {} + }, + "postcss-js": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.0.tgz", + "integrity": "sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "requires": {} + }, + "postcss-merge-longhand": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", + "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.0" + } + }, + "postcss-merge-rules": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", + "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", + "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "requires": { + "browserslist": "^4.16.6", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nested": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-5.0.6.tgz", + "integrity": "sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==", + "requires": { + "postcss-selector-parser": "^6.0.6" + } + }, + "postcss-nesting": { + "version": "10.1.10", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", + "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "requires": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", + "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "requires": { + "browserslist": "^4.16.6", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==" + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.7.2.tgz", + "integrity": "sha512-1q0ih7EDsZmCb/FMDRvosna7Gsbdx8CvYO5hYT120hcp2ZAuOHpSzibujZ4JpIUcAC02PG6b+eftxqjTFh5BNA==", + "requires": { + "@csstools/postcss-cascade-layers": "^1.0.4", + "@csstools/postcss-color-function": "^1.1.0", + "@csstools/postcss-font-format-keywords": "^1.0.0", + "@csstools/postcss-hwb-function": "^1.0.1", + "@csstools/postcss-ic-unit": "^1.0.0", + "@csstools/postcss-is-pseudo-class": "^2.0.6", + "@csstools/postcss-normalize-display-values": "^1.0.0", + "@csstools/postcss-oklab-function": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.1", + "@csstools/postcss-unset-value": "^1.0.1", + "autoprefixer": "^10.4.7", + "browserslist": "^4.21.0", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^6.6.3", + "postcss-attribute-case-insensitive": "^5.0.1", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.3", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.0", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.4", + "postcss-double-position-gradients": "^3.1.1", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.3", + "postcss-image-set-function": "^4.0.6", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.0", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.9", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.3", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.4", + "postcss-pseudo-class-any-link": "^7.1.5", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-reduce-initial": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", + "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "requires": { + "browserslist": "^4.16.6", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "requires": {} + }, + "postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true + }, + "postcss-safe-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", + "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + } + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "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==" + } + } + }, + "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==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "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" + } + }, + "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==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "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==" + } + } + }, + "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==" + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "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==" + }, + "raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "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==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "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-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "requires": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "dependencies": { + "promise": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz", + "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==", + "requires": { + "asap": "~2.0.6" + } + } + } + }, + "react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "requires": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "react-compound-slider": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-3.4.0.tgz", + "integrity": "sha512-KSje/rB0xSvvcb7YV0+82hkiXTV5ljSS7axKrNiXLf9AEO+rrr1Xq4MJWA+6v030YNNo/RoSoEB6D6fnoy+8ng==", + "requires": { + "@babel/runtime": "^7.12.5", + "d3-array": "^2.8.0", + "warning": "^4.0.3" + } + }, + "react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + } + } + }, + "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-error-boundary": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz", + "integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5" + } + }, + "react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "react-icons": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz", + "integrity": "sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg==", + "requires": {} + }, + "react-infinite-scroll-component": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", + "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", + "requires": { + "throttle-debounce": "^2.1.0" + } + }, + "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==" + }, + "react-json-editor-ajrm": { + "version": "2.5.13", + "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.13.tgz", + "integrity": "sha512-uYRJFzY34w7coLxeWPFZGyQpWdBKK5e8R9jBZTJ5gAFp3WuGVG2DdGZ8oJKOVJy0hqkxS9DzJIzGmmxHHQ9afA==", + "requires": { + "@babel/runtime": "^7.0.0-rc.0" + } + }, + "react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "requires": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + } + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "requires": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "dependencies": { + "react-fast-compare": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", + "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" + } + } + }, + "react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" + }, + "react-resize-detector": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-8.1.0.tgz", + "integrity": "sha512-S7szxlaIuiy5UqLhLL1KY3aoyGHbZzsTpYal9eYMwCyKqoqoVLCmIgAgNyIM1FhnP2KyBygASJxdhejrzjMb+w==", + "requires": { + "lodash": "^4.17.21" + } + }, + "react-router": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz", + "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==", + "requires": { + "@remix-run/router": "1.19.1" + } + }, + "react-router-dom": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz", + "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==", + "requires": { + "@remix-run/router": "1.19.1", + "react-router": "6.26.1" + } + }, + "react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "requires": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "fsevents": "^2.3.2", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "dependencies": { + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, + "@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } + } + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==" + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + } + }, + "jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==" + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==" + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "dependencies": { + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "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" + } + } + } + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + }, + "string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "requires": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==" + } + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + } + } + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "requires": { + "punycode": "^2.1.1" + } + }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "requires": {} + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + } + } + }, + "react-select": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz", + "integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==", + "requires": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0" + } + }, + "react-share": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.0.tgz", + "integrity": "sha512-POe8Ge/JT9Ew9iyW7CiYsCCWCb8uMJWqFl9S7W0fJ/oH5gBJNzukH0bL5vSr17KKG5h15d3GfKaoviI22BKeYA==", + "requires": { + "classnames": "^2.2.5", + "jsonp": "^0.2.1" + } + }, + "react-smooth": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.3.tgz", + "integrity": "sha512-yl4y3XiMorss7ayF5QnBiSprig0+qFHui8uh7Hgg46QX5O+aRMRKlfGGNGLHno35JkQSvSYY8eCWkBfHfrSHfg==", + "requires": { + "fast-equals": "^5.0.0", + "react-transition-group": "2.9.0" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } + } + }, + "react-table": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "requires": {} + }, + "react-textarea-autosize": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz", + "integrity": "sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==", + "requires": { + "@babel/runtime": "^7.10.2", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + } + }, + "react-top-loading-bar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-top-loading-bar/-/react-top-loading-bar-2.1.0.tgz", + "integrity": "sha512-07IPCC4fThfkH5PHm7d49s9UAq2rpy4RAyD+5gtwtBbBrwxkvcff7ZlbCgzrBXq8AvcGafDkpjcGdntJ4F0O9A==", + "requires": {} + }, + "react-transition-group": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", + "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, + "react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "requires": {} + }, + "react-use": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.4.0.tgz", + "integrity": "sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==", + "requires": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.3.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==" + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + } + } + }, + "reactstrap": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.0.tgz", + "integrity": "sha512-WWLTEG00qYav0E55PorWHReYTkz5IqkVmQNy0h6U81yqjSp9fOLFGV5pYSVeAUz+yRhU/RTE0oAWy22zr6sOIw==", + "requires": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.6.0", + "classnames": "^2.2.3", + "prop-types": "^15.5.8", + "react-popper": "^2.2.4", + "react-transition-group": "^4.4.2" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, + "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" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "recharts": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.2.tgz", + "integrity": "sha512-dVhNfgI21LlF+4AesO3mj+i+9YdAAjoGaDWIctUgH/G2iy14YVtb/DSUeic77xr19rbKCiq+pQGfeg2kJQDHig==", + "requires": { + "classnames": "^2.2.5", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^8.0.4", + "react-smooth": "^2.0.2", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8", + "victory-vendor": "^36.6.8" + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, + "recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "requires": { + "minimatch": "^3.0.5" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + }, + "dependencies": { + "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==" + } + } + }, + "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.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "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==", + "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==" + }, + "regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "requires": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "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==" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "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": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + } + } + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "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" + } + }, + "rollup": { + "version": "2.77.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz", + "integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==", + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "dependencies": { + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + } + } + }, + "rtl-css-js": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.15.0.tgz", + "integrity": "sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "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-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "sass": { + "version": "1.77.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", + "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "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": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.0.1.tgz", + "integrity": "sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==", + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "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==", + "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==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "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==", + "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==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "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==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, + "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==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==" + }, + "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==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "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==", + "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==" + }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "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==" + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "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==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" + }, + "source-map-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.1.tgz", + "integrity": "sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==", + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "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==", + "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" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "requires": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==" + } + } + }, + "stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "requires": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "requires": { + "internal-slot": "^1.0.4" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "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==" + } + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "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" + }, + "dependencies": { + "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==" + } + } + }, + "string.prototype.matchall": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "regexp.prototype.flags": "^1.4.3", + "side-channel": "^1.0.4" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + }, + "strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "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==" + }, + "style-loader": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "requires": {} + }, + "stylehacks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", + "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "requires": { + "browserslist": "^4.16.6", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylelint": { + "version": "16.8.2", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.8.2.tgz", + "integrity": "sha512-fInKATippQhcSm7AB+T32GpI+626yohrg33GkFT/5jzliUw5qhlwZq2UQQwgl3HsHrf09oeARi0ZwgY/UWEv9A==", + "dev": true, + "requires": { + "@csstools/css-parser-algorithms": "^3.0.0", + "@csstools/css-tokenizer": "^3.0.0", + "@csstools/media-query-list-parser": "^3.0.0", + "@csstools/selector-specificity": "^4.0.0", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.2", + "css-tree": "^2.3.1", + "debug": "^4.3.6", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^9.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^5.3.2", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.34.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.7", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.1", + "postcss": "^8.4.41", + "postcss-resolve-nested-selector": "^0.1.6", + "postcss-safe-parser": "^7.0.0", + "postcss-selector-parser": "^6.1.2", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^7.1.0", + "supports-hyperlinks": "^3.0.0", + "svg-tags": "^1.0.0", + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" + }, + "dependencies": { + "@csstools/selector-specificity": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-4.0.0.tgz", + "integrity": "sha512-189nelqtPd8++phaHNwYovKZI0FOzH1vQEE3QhHHkNIGrg5fSs9CbYP3RvfEH5geztnIA9Jwq91wyOIwAW5JIQ==", + "dev": true, + "requires": {} + }, + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "requires": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + } + }, + "css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "requires": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + } + }, + "file-entry-cache": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "dev": true, + "requires": { + "flat-cache": "^5.0.0" + } + }, + "flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "requires": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + } + } + } + }, + "stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.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==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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==" + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + } + } + }, + "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==" + }, + "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==" + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "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" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "tailwindcss": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.1.6.tgz", + "integrity": "sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg==", + "requires": { + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "color-name": "^1.1.4", + "detective": "^5.2.1", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "lilconfig": "^2.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.14", + "postcss-import": "^14.1.0", + "postcss-js": "^4.0.0", + "postcss-load-config": "^3.1.4", + "postcss-nested": "5.0.6", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", + "quick-lru": "^5.1.1", + "resolve": "^1.22.1" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + }, + "tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "requires": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "dependencies": { + "supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + } + } + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "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==" + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "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==" + }, + "throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" + }, + "throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "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==" + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "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-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==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "requires": { + "minimist": "^1.2.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==" + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true + }, + "ua-parser-js": { + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz", + "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw==" + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "requires": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + } + }, + "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" + } + }, + "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==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "requires": {} + }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} + }, + "use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "requires": { + "use-isomorphic-layout-effect": "^1.1.1" + } + }, + "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-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "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==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "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==" + }, + "v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + } + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "victory-vendor": { + "version": "36.6.8", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.8.tgz", + "integrity": "sha512-H3kyQ+2zgjMPvbPqAl7Vwm2FD5dU7/4bCTQakFQnpIsfDljeOMDojRsrmJfwh4oAlNnWhpAf+mbAoLh8u7dwyQ==", + "requires": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + }, + "dependencies": { + "d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", + "requires": { + "internmap": "1 - 2" + } + } + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "requires": { + "makeerror": "1.0.12" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "webpack": { + "version": "5.73.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz", + "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.9.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, + "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" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz", + "integrity": "sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "requires": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "dependencies": { + "webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + } + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "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==" + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-fetch": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", + "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "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" + }, + "dependencies": { + "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==" + } + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "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==", + "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-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" + }, + "workbox-background-sync": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.5.3.tgz", + "integrity": "sha512-0DD/V05FAcek6tWv9XYj2w5T/plxhDSpclIcAGjA/b7t/6PdaRkQ7ZgtAX6Q/L7kV7wZ8uYRJUoH11VjNipMZw==", + "requires": { + "idb": "^6.1.4", + "workbox-core": "6.5.3" + } + }, + "workbox-broadcast-update": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.5.3.tgz", + "integrity": "sha512-4AwCIA5DiDrYhlN+Miv/fp5T3/whNmSL+KqhTwRBTZIL6pvTgE4lVuRzAt1JltmqyMcQ3SEfCdfxczuI4kwFQg==", + "requires": { + "workbox-core": "6.5.3" + } + }, + "workbox-build": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.5.3.tgz", + "integrity": "sha512-8JNHHS7u13nhwIYCDea9MNXBNPHXCs5KDZPKI/ZNTr3f4sMGoD7hgFGecbyjX1gw4z6e9bMpMsOEJNyH5htA/w==", + "requires": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.5.3", + "workbox-broadcast-update": "6.5.3", + "workbox-cacheable-response": "6.5.3", + "workbox-core": "6.5.3", + "workbox-expiration": "6.5.3", + "workbox-google-analytics": "6.5.3", + "workbox-navigation-preload": "6.5.3", + "workbox-precaching": "6.5.3", + "workbox-range-requests": "6.5.3", + "workbox-recipes": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3", + "workbox-streams": "6.5.3", + "workbox-sw": "6.5.3", + "workbox-window": "6.5.3" + }, + "dependencies": { + "@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "requires": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + } + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "requires": { + "whatwg-url": "^7.0.0" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "requires": { + "punycode": "^2.1.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "workbox-cacheable-response": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.5.3.tgz", + "integrity": "sha512-6JE/Zm05hNasHzzAGKDkqqgYtZZL2H06ic2GxuRLStA4S/rHUfm2mnLFFXuHAaGR1XuuYyVCEey1M6H3PdZ7SQ==", + "requires": { + "workbox-core": "6.5.3" + } + }, + "workbox-core": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.3.tgz", + "integrity": "sha512-Bb9ey5n/M9x+l3fBTlLpHt9ASTzgSGj6vxni7pY72ilB/Pb3XtN+cZ9yueboVhD5+9cNQrC9n/E1fSrqWsUz7Q==" + }, + "workbox-expiration": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.5.3.tgz", + "integrity": "sha512-jzYopYR1zD04ZMdlbn/R2Ik6ixiXbi15c9iX5H8CTi6RPDz7uhvMLZPKEndZTpfgmUk8mdmT9Vx/AhbuCl5Sqw==", + "requires": { + "idb": "^6.1.4", + "workbox-core": "6.5.3" + } + }, + "workbox-google-analytics": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.5.3.tgz", + "integrity": "sha512-3GLCHotz5umoRSb4aNQeTbILETcrTVEozSfLhHSBaegHs1PnqCmN0zbIy2TjTpph2AGXiNwDrWGF0AN+UgDNTw==", + "requires": { + "workbox-background-sync": "6.5.3", + "workbox-core": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3" + } + }, + "workbox-navigation-preload": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.5.3.tgz", + "integrity": "sha512-bK1gDFTc5iu6lH3UQ07QVo+0ovErhRNGvJJO/1ngknT0UQ702nmOUhoN9qE5mhuQSrnK+cqu7O7xeaJ+Rd9Tmg==", + "requires": { + "workbox-core": "6.5.3" + } + }, + "workbox-precaching": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.5.3.tgz", + "integrity": "sha512-sjNfgNLSsRX5zcc63H/ar/hCf+T19fRtTqvWh795gdpghWb5xsfEkecXEvZ8biEi1QD7X/ljtHphdaPvXDygMQ==", + "requires": { + "workbox-core": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3" + } + }, + "workbox-range-requests": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.5.3.tgz", + "integrity": "sha512-pGCP80Bpn/0Q0MQsfETSfmtXsQcu3M2QCJwSFuJ6cDp8s2XmbUXkzbuQhCUzKR86ZH2Vex/VUjb2UaZBGamijA==", + "requires": { + "workbox-core": "6.5.3" + } + }, + "workbox-recipes": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.5.3.tgz", + "integrity": "sha512-IcgiKYmbGiDvvf3PMSEtmwqxwfQ5zwI7OZPio3GWu4PfehA8jI8JHI3KZj+PCfRiUPZhjQHJ3v1HbNs+SiSkig==", + "requires": { + "workbox-cacheable-response": "6.5.3", + "workbox-core": "6.5.3", + "workbox-expiration": "6.5.3", + "workbox-precaching": "6.5.3", + "workbox-routing": "6.5.3", + "workbox-strategies": "6.5.3" + } + }, + "workbox-routing": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.3.tgz", + "integrity": "sha512-DFjxcuRAJjjt4T34RbMm3MCn+xnd36UT/2RfPRfa8VWJGItGJIn7tG+GwVTdHmvE54i/QmVTJepyAGWtoLPTmg==", + "requires": { + "workbox-core": "6.5.3" + } + }, + "workbox-strategies": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.3.tgz", + "integrity": "sha512-MgmGRrDVXs7rtSCcetZgkSZyMpRGw8HqL2aguszOc3nUmzGZsT238z/NN9ZouCxSzDu3PQ3ZSKmovAacaIhu1w==", + "requires": { + "workbox-core": "6.5.3" + } + }, + "workbox-streams": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.5.3.tgz", + "integrity": "sha512-vN4Qi8o+b7zj1FDVNZ+PlmAcy1sBoV7SC956uhqYvZ9Sg1fViSbOpydULOssVJ4tOyKRifH/eoi6h99d+sJ33w==", + "requires": { + "workbox-core": "6.5.3", + "workbox-routing": "6.5.3" + } + }, + "workbox-sw": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.5.3.tgz", + "integrity": "sha512-BQBzm092w+NqdIEF2yhl32dERt9j9MDGUTa2Eaa+o3YKL4Qqw55W9yQC6f44FdAHdAJrJvp0t+HVrfh8AiGj8A==" + }, + "workbox-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-Es8Xr02Gi6Kc3zaUwR691ZLy61hz3vhhs5GztcklQ7kl5k2qAusPh0s6LF3wEtlpfs9ZDErnmy5SErwoll7jBA==", + "requires": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.5.3" + }, + "dependencies": { + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "workbox-window": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.5.3.tgz", + "integrity": "sha512-GnJbx1kcKXDtoJBVZs/P7ddP0Yt52NNy4nocjBpYPiRhMqTpJCNrSL+fGHZ/i/oP6p/vhE8II0sA6AZGKGnssw==", + "requires": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.5.3" + } + }, + "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" + } + }, + "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": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "requires": { + "use-sync-external-store": "1.2.0" + } + } + } +} diff --git a/Submodules/GreedyBear/frontend/package.json b/Submodules/GreedyBear/frontend/package.json new file mode 100644 index 0000000..f4c9d88 --- /dev/null +++ b/Submodules/GreedyBear/frontend/package.json @@ -0,0 +1,83 @@ +{ + "name": "frontend", + "version": "0.1.0", + "private": true, + "proxy": "http://localhost:80/", + "config": { + "eslint": ".github/configurations/node_linters/eslint/.eslintrc.json", + "stylelint": ".github/configurations/node_linters/stylelint/.stylelintrc.json", + "prettier": ".github/configurations/node_linters/prettier/.prettierrc.js" + }, + "dependencies": { + "@certego/certego-ui": "^0.1.10", + "axios": "^1.6.0", + "axios-hooks": "^3.0.4", + "bootstrap": ">=5.3.0", + "formik": "^2.2.9", + "prop-types": "^15.8.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-icons": "^4.9.0", + "react-router-dom": "^6.26.0", + "react-scripts": "^5.0.1", + "react-table": "^7.8.0", + "react-use": "^17.4.0", + "reactstrap": "^9.2.0", + "recharts": "^2.6.2", + "sass": "^1.72.0", + "zustand": "^4.5.2" + }, + "scripts": { + "start": "PORT=3001 react-scripts start", + "build": "react-scripts build", + "eject": "react-scripts eject", + "test": "TZ=UTC jest ./tests", + "test-coverage": "npm test -- --coverage=true", + "lint-config-install": "cd ../.github/configurations/node_linters/eslint && npm i", + "lint": "eslint 'src/**/*.{js,jsx}' 'tests/**/*.{js,jsx}'", + "lint-fix": "npm run lint -- --fix", + "lint-scss-config-install": "cd ../.github/configurations/node_linters/stylelint && npm i", + "lint-scss": "stylelint --config ../.github/configurations/node_linters/stylelint/.stylelintrc.json 'src/styles/**/*.{css,scss}'", + "lint-scss-fix": "npm run lint-scss -- --fix", + "formatter": "prettier 'src/**/*.{js,jsx}' 'tests/**/*.{js,jsx}' 'src/styles/*.{css,scss}' --check", + "formatter-fix": "npm run formatter -- --write" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.22.5", + "@testing-library/jest-dom": "^6.4.8", + "@testing-library/react": "^12.1.5", + "@testing-library/react-hooks": "^8.0.1", + "@testing-library/user-event": "^14.0.0", + "babel-eslint": "^10.1.0", + "babel-jest": "^29.7.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "prettier": "2.8.4", + "stylelint": "^16.8.1" + } +} diff --git a/Submodules/GreedyBear/frontend/public/favicon.ico b/Submodules/GreedyBear/frontend/public/favicon.ico new file mode 100644 index 0000000..a5b3967 Binary files /dev/null and b/Submodules/GreedyBear/frontend/public/favicon.ico differ diff --git a/Submodules/GreedyBear/frontend/public/greedybear.png b/Submodules/GreedyBear/frontend/public/greedybear.png new file mode 100644 index 0000000..887aada Binary files /dev/null and b/Submodules/GreedyBear/frontend/public/greedybear.png differ diff --git a/Submodules/GreedyBear/frontend/public/index.html b/Submodules/GreedyBear/frontend/public/index.html new file mode 100644 index 0000000..9b1db2f --- /dev/null +++ b/Submodules/GreedyBear/frontend/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + GreedyBear + + + +
+ + + diff --git a/Submodules/GreedyBear/frontend/public/manifest.json b/Submodules/GreedyBear/frontend/public/manifest.json new file mode 100644 index 0000000..b79c394 --- /dev/null +++ b/Submodules/GreedyBear/frontend/public/manifest.json @@ -0,0 +1,16 @@ +{ + "short_name": "GreedyBear", + "name": "GreedyBear", + "theme_color": "#0b2b38", + "background_color": "#00161f", + "display": "standalone", + "orientation": "portrait", + "start_url": ".", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ] +} diff --git a/Submodules/GreedyBear/frontend/public/robots.txt b/Submodules/GreedyBear/frontend/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/Submodules/GreedyBear/frontend/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/Submodules/GreedyBear/frontend/src/App.jsx b/Submodules/GreedyBear/frontend/src/App.jsx new file mode 100644 index 0000000..84a0e13 --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/App.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import { BrowserRouter } from "react-router-dom"; + +// layout +import AppMain from "./layouts/AppMain"; +import AppFooter from "./layouts/AppFooter"; + +function App() { + return ( + + + + + ); +} + +export default App; diff --git a/Submodules/GreedyBear/frontend/src/components/Routes.jsx b/Submodules/GreedyBear/frontend/src/components/Routes.jsx new file mode 100644 index 0000000..afee3d9 --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/Routes.jsx @@ -0,0 +1,107 @@ +import React, { Suspense } from "react"; +import { FallBackLoading } from "@certego/certego-ui"; + +import IfAuthRedirectGuard from "../wrappers/ifAuthRedirectGuard"; +import AuthGuard from "../wrappers/AuthGuard"; + +const Home = React.lazy(() => import("./home/Home")); +const Login = React.lazy(() => import("./auth/Login")); +const Logout = React.lazy(() => import("./auth/Logout")); +const Register = React.lazy(() => import("./auth/Register")); +const EmailVerification = React.lazy(() => import("./auth/EmailVerification")); +const ResetPassword = React.lazy(() => import("./auth/ResetPassword")); +const Dashboard = React.lazy(() => import("./dashboard/Dashboard")); +const Sessions = React.lazy(() => import("./me/sessions/Sessions")); +const Feeds = React.lazy(() => import("./feeds/Feeds")); + +// public components +const publicRoutesLazy = [ + /* Home */ + { + index: true, + element: ( + }> + + + ), + }, + /* Dashboard */ + { + path: "/dashboard", + element: ( + }> + + + ), + }, + /* Feeds */ + { + path: "/feeds", + element: ( + }> + + + ), + }, +].map((r) => ({ + ...r, + element: }>{r.element}, +})); + +// no auth public components +const noAuthRoutesLazy = [ + { + path: "/login", + element: , + }, + { + path: "/register", + element: , + }, + { + path: "/verify-email", + element: , + }, + { + path: "/reset-password", + element: , + }, +].map((r) => ({ + ...r, + element: ( + + }>{r.element} + + ), +})); + +// auth components +const authRoutesLazy = [ + /* auth */ + { + path: "/logout", + element: ( + }> + + + ), + }, + /* API Access/Sessions Management */ + { + path: "/me/sessions", + element: ( + }> + + + ), + }, +].map((r) => ({ + ...r, + element: ( + + }>{r.element} + + ), +})); + +export { publicRoutesLazy, noAuthRoutesLazy, authRoutesLazy }; diff --git a/Submodules/GreedyBear/frontend/src/components/auth/EmailVerification.jsx b/Submodules/GreedyBear/frontend/src/components/auth/EmailVerification.jsx new file mode 100644 index 0000000..717f94e --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/EmailVerification.jsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Navigate, useSearchParams } from "react-router-dom"; +import { Spinner } from "reactstrap"; +import { ContentSection } from "@certego/certego-ui"; + +import { verifyEmail } from "./api"; + +// Component +export default function EmailVerification() { + console.debug("EmailVerification rendered!"); + + // search param + const [searchParams] = useSearchParams(); + const key = searchParams.get("key") || null; + console.debug("key:", key); + + // local state + const [isKeyValid, setIsKeyValid] = React.useState(true); + const [isVerified, setIsVerified] = React.useState(false); + + // side-effects + React.useEffect(() => { + if (key) { + setTimeout( + () => + verifyEmail({ key }) + .then(() => setIsVerified(true)) + .catch(() => setIsKeyValid(false)), + 500 + ); + } else { + setIsKeyValid(false); + } + }, [key]); + + return isVerified ? ( + + ) : ( + + {isKeyValid ? ( +
+ +

Verifying...

+
+ ) : ( +
Error: Invalid key.
+ )} +
+ ); +} diff --git a/Submodules/GreedyBear/frontend/src/components/auth/Login.jsx b/Submodules/GreedyBear/frontend/src/components/auth/Login.jsx new file mode 100644 index 0000000..cb770f3 --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/Login.jsx @@ -0,0 +1,136 @@ +import React from "react"; +import { + FormGroup, + Label, + Container, + Input, + Spinner, + Button, + Row, +} from "reactstrap"; +import { Form, Formik } from "formik"; + +import { ContentSection } from "@certego/certego-ui"; + +import { useAuthStore } from "../../stores"; +import { + ResendVerificationEmailButton, + ForgotPasswordButton, +} from "./utils/registration-buttons"; + +// constants +const initialValues = { + username: "", + password: "", +}; + +// methods +const onValidate = (values) => { + const errors = {}; + if (!values.username) { + errors.username = "Required"; + } + if (!values.password) { + errors.password = "Required"; + } + return errors; +}; + +// Component +function Login() { + console.debug("Login rendered!"); + + // local state + const [passwordShown, setPasswordShown] = React.useState(false); + + // auth store + const loginUser = useAuthStore( + React.useCallback((s) => s.service.loginUser, []) + ); + + // callbacks + const onSubmit = React.useCallback( + async (values, _formik) => { + try { + await loginUser(values); + } catch (e) { + // handled inside loginUser + } + }, + [loginUser] + ); + + return ( + + + +

Log In

+
+ {/* Form */} + + {(formik) => ( +
+ {/* username */} + + + + + {/* password */} + + + + + + setPasswordShown(!passwordShown)} + /> + + + {/* Submit */} + + + +
+ )} +
+
+ {/* popover buttons */} + + + + +
+
+ ); +} + +export default Login; diff --git a/Submodules/GreedyBear/frontend/src/components/auth/Logout.jsx b/Submodules/GreedyBear/frontend/src/components/auth/Logout.jsx new file mode 100644 index 0000000..0b8e24b --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/Logout.jsx @@ -0,0 +1,21 @@ +import React from "react"; + +import { FallBackLoading } from "@certego/certego-ui"; + +import { useAuthStore } from "../../stores"; +import { AUTHENTICATION_STATUSES } from "../../constants"; + +export default function Logout() { + // auth store + const [isAuthenticated, logoutUser] = useAuthStore( + React.useCallback((s) => [s.isAuthenticated, s.service.logoutUser], []) + ); + + React.useEffect(() => { + if (isAuthenticated === AUTHENTICATION_STATUSES.TRUE) { + logoutUser(); + } + }, [isAuthenticated, logoutUser]); + + return ; +} diff --git a/Submodules/GreedyBear/frontend/src/components/auth/Register.jsx b/Submodules/GreedyBear/frontend/src/components/auth/Register.jsx new file mode 100644 index 0000000..7d9afac --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/Register.jsx @@ -0,0 +1,534 @@ +import React from "react"; +import { + Container, + Row, + Col, + FormGroup, + Label, + Spinner, + Input, + InputGroup, + Button, + InputGroupText, +} from "reactstrap"; +import { Form, Formik } from "formik"; +import useTitle from "react-use/lib/useTitle"; + +import { ContentSection, Select } from "@certego/certego-ui"; + +import { + AfterRegistrationModalAlert, + InviteOnlyAlert, + ConfigurationModalAlert, +} from "./utils/registration-alert"; +import { registerUser, checkConfiguration } from "./api"; +import { + EmailValidator, + PasswordValidator, + UserFieldsValidator, + ProfileValidator, + UsernameValidator, + ComparePassword, +} from "./utils/validator"; + +// constants +const hearAboutUsChoices = [ + { + label: "Other", + value: "other", + }, + { + label: "Search Engine (Google, DuckDuckGo, etc.)", + value: "search_engine", + }, + { + label: "Recommended by friend or colleague", + value: "was_recommended", + }, + { + label: "Social media", + value: "social_media", + }, + { + label: "Blog or Publication", + value: "blog_or_publication", + }, +]; + +const INITIAL_VALUES = { + first_name: "", + last_name: "", + email: "", + username: "", + password: "", + confirmPassword: "", + company_name: "", + company_role: "", + twitter_handle: "", + discover_from: "other", +}; + +const REGISTRATION_FORM_STORAGE_KEY = "registrationForm"; +const initialValues = + JSON.parse(localStorage.getItem(REGISTRATION_FORM_STORAGE_KEY, "{}")) || + INITIAL_VALUES; + +console.debug("initialValues", initialValues); + +const onValidate = (values) => { + const errors = {}; + + // static text fields + ["first_name", "last_name"].forEach((field) => { + const userErrors = UserFieldsValidator(field, values[field]); + if (userErrors[field]) { + errors[field] = userErrors[field]; + } + }); + + // username + const usernameErrors = UsernameValidator(values.username); + if (usernameErrors.username) { + errors.username = usernameErrors.username; + } + + // email + const emailErrors = EmailValidator(values.email); + if (emailErrors.email) { + errors.email = emailErrors.email; + } + + // profile + ["company_name", "company_role"].forEach((field) => { + const profileErrors = ProfileValidator(field, values[field]); + if (profileErrors[field]) { + errors[field] = profileErrors[field]; + } + }); + + // store in localStorage so user doesn't have to fill all fields again + localStorage.setItem( + REGISTRATION_FORM_STORAGE_KEY, + JSON.stringify({ + ...values, + password: "", + confirmPassword: "", + }) + ); + Object.keys(initialValues).forEach((key) => { + initialValues[key] = values[key]; + }); + + // password fields + const passwordErrors = PasswordValidator(values.password); + if (passwordErrors.password) { + errors.password = passwordErrors.password; + } + const confirmPasswordErrors = PasswordValidator(values.confirmPassword); + if (confirmPasswordErrors.password) { + errors.confirmPassword = confirmPasswordErrors.password; + } + const comparePasswordErrors = ComparePassword( + values.password, + values.confirmPassword + ); + if (comparePasswordErrors.password) { + errors.password = comparePasswordErrors.password; + } + if (comparePasswordErrors.confirmPassword) { + errors.confirmPassword = comparePasswordErrors.confirmPassword; + } + + // console.debug("Errors", errors); + return errors; +}; + +// Component +export default function Register() { + console.debug("Register rendered!"); + + // page title + useTitle("GreedyBear | Sign up", { restoreOnUnmount: true }); + + // local state + const [showAfterRegistrationModal, setShowAfterRegistrationModal] = + React.useState(false); + const [showConfigurationModal, setShowConfigurationModal] = + React.useState(false); + const [passwordShown, setPasswordShown] = React.useState(false); + + console.debug("showAfterRegistrationModal:", showAfterRegistrationModal); + + React.useEffect(() => { + checkConfiguration({ + params: { + page: "register", + }, + }).catch((e) => { + console.debug(e); + setShowConfigurationModal(true); + }); + }, []); + + console.debug("showConfigurationModal:", showConfigurationModal); + + // callbacks + const onSubmit = React.useCallback( + async (values) => { + const reqBody = { + first_name: values.first_name, + last_name: values.last_name, + username: values.username, + email: values.email, + password: values.password, + profile: { + company_name: values.company_name, + company_role: values.company_role, + twitter_handle: values.twitter_handle, + discover_from: values.discover_from, + }, + }; + try { + await registerUser(reqBody); + + // deleted user data after successful registration + localStorage.removeItem(REGISTRATION_FORM_STORAGE_KEY); + Object.keys(INITIAL_VALUES).forEach((key) => { + initialValues[key] = INITIAL_VALUES[key]; + }); + + setShowAfterRegistrationModal(true); + } catch (e) { + // handled inside registerUser + } + }, + [setShowAfterRegistrationModal] + ); + + return ( + + {showConfigurationModal && ( + + )} + {showAfterRegistrationModal && ( + + )} + + + + + + +

Register

+
+
+ {/* Form */} + + {(formik) => ( +
+ {/* Name */} + + + + + {formik.touched.first_name && ( + {formik.errors.first_name} + )} + + + + + {formik.touched.last_name && ( + {formik.errors.last_name} + )} + + + {/* Email/Username */} + + + + + {formik.touched.email && ( + {formik.errors.email} + )} + + + + + {formik.touched.username && ( + {formik.errors.username} + )} + + + {/* Password */} + + + + + {formik.touched.password && ( + {formik.errors.password} + )} + + + + + {formik.touched.confirmPassword && ( + {formik.errors.confirmPassword} + )} + + + + setPasswordShown(!passwordShown)} + /> + + + + We ask you to provide the following information to better + understand what you intend to use GreedyBear for + + {/* Extra fields */} + + + + + {formik.touched.company_name && ( + {formik.errors.company_name} + )} + + + + + {formik.touched.company_role && ( + {formik.errors.company_role} + )} + + + + + + + @ + + + + + + + {formik.touched.password && ( + {formik.errors.password} + )} + + + + + + + {formik.touched.confirmPassword && ( + {formik.errors.confirmPassword} + )} + + + + setPasswordShown(!passwordShown)} + /> + + + {/* Submit */} + + + +
+ )} +
+
+ ) : ( +
Error: Invalid key.
+ )} +
+ ); +} diff --git a/Submodules/GreedyBear/frontend/src/components/auth/api.js b/Submodules/GreedyBear/frontend/src/components/auth/api.js new file mode 100644 index 0000000..7430a52 --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/api.js @@ -0,0 +1,76 @@ +import axios from "axios"; + +import { addToast } from "@certego/certego-ui"; + +import { AUTH_BASE_URI } from "../../constants/api"; + +export async function registerUser(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/register`, body); + return resp; + } catch (err) { + addToast("Registration failed!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function verifyEmail(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/verify-email`, body); + addToast( + "Your email has been succesfully verified!", + null, + "success", + true + ); + return resp; + } catch (err) { + addToast("Email verification failed!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function resendVerificationMail(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/resend-verification`, body); + addToast("Verification email sent!", null, "success"); + return resp; + } catch (err) { + addToast("Failed to send email!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function requestPasswordReset(body) { + try { + const resp = await axios.post( + `${AUTH_BASE_URI}/request-password-reset`, + body + ); + addToast("Email sent!", null, "success"); + return resp; + } catch (err) { + addToast("Failed to send email!", err.parsedMsg, "danger", true); + return null; + } +} + +export async function resetPassword(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/reset-password`, body); + addToast("Password reset successfully!", null, "success", true); + return resp; + } catch (err) { + addToast("Password reset failed!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function checkConfiguration(body) { + try { + const resp = await axios.get(`${AUTH_BASE_URI}/configuration`, body); + return resp; + } catch (err) { + return Promise.reject(err); + } +} diff --git a/Submodules/GreedyBear/frontend/src/components/auth/utils/EmailForm.jsx b/Submodules/GreedyBear/frontend/src/components/auth/utils/EmailForm.jsx new file mode 100644 index 0000000..9211ab4 --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/utils/EmailForm.jsx @@ -0,0 +1,86 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { FormGroup, Label, Input, Button, Spinner } from "reactstrap"; +import { Form, Formik } from "formik"; + +import { EmailValidator } from "./validator"; + +// constants +const initialValues = { + email: "", +}; + +// methods +const onValidate = (values) => { + const errors = {}; + // email + const emailErrors = EmailValidator(values.email); + if (emailErrors.email) { + errors.email = emailErrors.email; + } + return errors; +}; + +// Resend Verification Email Form +export default function EmailForm({ onFormSubmit, apiCallback, ...restProps }) { + // callbacks + const onSubmit = React.useCallback( + async (values) => { + try { + await apiCallback(values); + onFormSubmit(); + } catch (e) { + // error will be handled by apiCallback + } + }, + [apiCallback, onFormSubmit] + ); + + return ( + + {(formik) => ( +
+ {/* Email field */} + + + + {formik.touched.email && {formik.errors.email}} + + {/* Submit */} + + + +
+ )} +
+ ); +} + +EmailForm.propTypes = { + onFormSubmit: PropTypes.func.isRequired, + apiCallback: PropTypes.func.isRequired, +}; diff --git a/Submodules/GreedyBear/frontend/src/components/auth/utils/registration-alert.jsx b/Submodules/GreedyBear/frontend/src/components/auth/utils/registration-alert.jsx new file mode 100644 index 0000000..3c4b91c --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/utils/registration-alert.jsx @@ -0,0 +1,137 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Modal, ModalHeader, ModalBody, Alert } from "reactstrap"; +import { MdInfoOutline } from "react-icons/md"; +import { useNavigate } from "react-router-dom"; +import { GREEDYBEAR_DOCS_URL } from "../../../constants/environment"; + +export function InviteOnlyAlert() { + return ( + +
+ +  Sign up below to join the waitlist! +
+

+ Please note that GreedyBear is operated as an invite-only trust group. + Once you sign up, our team will reach out to you at the provided email + address. +
+ + We recommend signing up with a business email address and not a + personal one to increase your chances of getting access. + +

+
+ ); +} + +export function AfterRegistrationModalAlert(props) { + // modal state from props + const { isOpen, setIsOpen } = props; + const navigate = useNavigate(); + + // callbacks + const toggle = React.useCallback(() => { + navigate("/"); + setIsOpen((o) => !o); + }, [navigate, setIsOpen]); + + return ( + + Registration successful! 🥳 + + <> +
+ +

Thank you for signing up on GreedyBear! 🤝

+
+
+
+ + Next Steps: + +
    +
  1. + Verify your email address. We have already sent you a{" "} + + link + + . +
  2. +
  3. Our team will reach out to you soon afterwards.
  4. +
+
+ +
+
+ ); +} + +export function ConfigurationModalAlert(props) { + const { isOpen, setIsOpen, title } = props; + const navigate = useNavigate(); + + // callbacks + const toggle = React.useCallback(() => { + navigate("/"); + setIsOpen((o) => !o); + }, [navigate, setIsOpen]); + + return ( + + Warning + + <> +
+ +

{title}

+
+
+
+

+ If you are an admin please check the{" "} + + documentation + {" "} + and correctly configure all the required variables. +

+
+ +
+
+ ); +} + +AfterRegistrationModalAlert.propTypes = { + isOpen: PropTypes.bool.isRequired, + setIsOpen: PropTypes.func.isRequired, +}; + +ConfigurationModalAlert.propTypes = { + isOpen: PropTypes.bool.isRequired, + setIsOpen: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, +}; diff --git a/Submodules/GreedyBear/frontend/src/components/auth/utils/registration-buttons.jsx b/Submodules/GreedyBear/frontend/src/components/auth/utils/registration-buttons.jsx new file mode 100644 index 0000000..954e19b --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/utils/registration-buttons.jsx @@ -0,0 +1,87 @@ +import React from "react"; +// import PropTypes from "prop-types"; +import { Alert, PopoverBody } from "reactstrap"; +import { IoMail } from "react-icons/io5"; +import { PopupFormButton } from "@certego/certego-ui"; +import EmailForm from "./EmailForm"; +import { resendVerificationMail, requestPasswordReset } from "../api"; + +function Icon(text) { + return ( + + +   + {text} + + ); +} + +function FormPopoverBody(formProps, text, api) { + return ( + + + +  {text} + + + + ); +} + +function EmailIcon() { + return Icon("Need Verification Email?"); +} + +function EmailFormPopoverBody(formProps) { + return FormPopoverBody( + formProps, + "We will shoot you an email with instructions to verify your email address.", + resendVerificationMail + ); +} + +// Popover Button for "Request Verification Email?" +export function ResendVerificationEmailButton() { + return ( + + ); +} + +function PasswordIcon() { + return Icon("Forgot Password?"); +} + +function PasswordFormPopoverBody(formProps) { + return FormPopoverBody( + formProps, + "We will shoot you an email with instructions to reset your password.", + requestPasswordReset + ); +} + +// Popover Button for "Forgot Password?" +export function ForgotPasswordButton() { + return ( + + ); +} diff --git a/Submodules/GreedyBear/frontend/src/components/auth/utils/validator.jsx b/Submodules/GreedyBear/frontend/src/components/auth/utils/validator.jsx new file mode 100644 index 0000000..6d114db --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/auth/utils/validator.jsx @@ -0,0 +1,77 @@ +import { + PASSWORD_REGEX, + EMAIL_REGEX, + HACKER_MEME_STRING, +} from "../../../constants"; + +export function ComparePassword(password, confirmPassword) { + const errors = {}; + if ( + password.length > 0 && + confirmPassword.length > 0 && + password !== confirmPassword + ) { + errors.password = "Passwords do not match."; + errors.confirmPassword = "Passwords do not match."; + } + return errors; +} + +export function PasswordValidator(password) { + const errors = {}; + if (!password) { + errors.password = "Required"; + } else if (password.length < 12) { + errors.password = "Must be 12 characters or more"; + } else if (!PASSWORD_REGEX.test(password)) { + errors.password = + "The password is entirely numeric or contains special characters"; + } + return errors; +} + +export function UserFieldsValidator(field, value) { + const errors = {}; + // text fields + if (!value) { + errors[field] = "Required"; + } else if (value.length > 15) { + errors[field] = "Must be 15 characters or less"; + } else if (value.length < 4) { + errors[field] = "Must be 4 characters or more"; + } + return errors; +} + +export function UsernameValidator(username) { + const errors = UserFieldsValidator("username", username); + if ( + ["administrator", "admin", "certego", "hacker"].indexOf(username) !== -1 + ) { + errors.username = HACKER_MEME_STRING; + } + return errors; +} + +export function ProfileValidator(field, value) { + const errors = {}; + // text fields + if (!value) { + errors[field] = "Required"; + } else if (value.length > 30) { + errors[field] = "Must be 30 characters or less"; + } else if (value.length < 3) { + errors[field] = "Must be 3 characters or more"; + } + return errors; +} + +export function EmailValidator(email) { + const errors = {}; + if (!email) { + errors.email = "Required"; + } else if (!EMAIL_REGEX.test(email)) { + errors.email = "Invalid email address"; + } + return errors; +} diff --git a/Submodules/GreedyBear/frontend/src/components/dashboard/Dashboard.jsx b/Submodules/GreedyBear/frontend/src/components/dashboard/Dashboard.jsx new file mode 100644 index 0000000..80aa62c --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/dashboard/Dashboard.jsx @@ -0,0 +1,107 @@ +import React from "react"; +import { Container, Row, Col } from "reactstrap"; + +import { + ElasticTimePicker, + useTimePickerStore, + SmallInfoCard, +} from "@certego/certego-ui"; + +import { + FeedsSourcesChart, + FeedsDownloadsChart, + EnrichmentSourcesChart, + EnrichmentRequestsChart, + FeedsTypesChart, +} from "./utils/charts"; + +const feedsChartList = [ + ["FeedsSourcesChart", "Feeds: Sources", FeedsSourcesChart], + ["FeedsDownloadsChart", "Feeds: Downloads", FeedsDownloadsChart], +]; + +const feedsTypesChartList = [ + ["FeedsTypesChart", "Feeds: Types", FeedsTypesChart], +]; + +const enrichmentChartList = [ + [ + "EnrichmentSourcesChart", + "Enrichment Service: Sources", + EnrichmentSourcesChart, + ], + [ + "EnrichmentRequestsChart", + "Enrichment Service: Requests", + EnrichmentRequestsChart, + ], +]; + +function Dashboard() { + console.debug("Dashboard rendered!"); + const { range, onTimeIntervalChange } = useTimePickerStore(); + + return ( + +
+

Dashboard

+ +
+ + {feedsTypesChartList.map(([id, header, Component]) => ( + + + + + } + style={{ minHeight: 360 }} + /> + + ))} + + + {feedsChartList.map(([id, header, Component]) => ( + + + + + } + style={{ minHeight: 360 }} + /> + + ))} + + + {enrichmentChartList.map(([id, header, Component]) => ( + + + + + } + style={{ minHeight: 360 }} + /> + + ))} + +
+ ); +} + +export default Dashboard; diff --git a/Submodules/GreedyBear/frontend/src/components/dashboard/utils/charts.jsx b/Submodules/GreedyBear/frontend/src/components/dashboard/utils/charts.jsx new file mode 100644 index 0000000..6239a8a --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/dashboard/utils/charts.jsx @@ -0,0 +1,151 @@ +import React from "react"; +import { Bar, Area } from "recharts"; + +import { AnyChartWidget, getRandomColorsArray } from "@certego/certego-ui"; +import { + FEEDS_STATISTICS_SOURCES_URI, + FEEDS_STATISTICS_DOWNLOADS_URI, + FEEDS_STATISTICS_TYPES_URI, + ENRICHMENT_STATISTICS_SOURCES_URI, + ENRICHMENT_STATISTICS_REQUESTS_URI, +} from "../../../constants/api"; + +import { FEED_COLOR_MAP, ENRICHMENT_COLOR_MAP } from "../../../constants"; + +// constants +const colors = getRandomColorsArray(30, true); + +export const FeedsSourcesChart = React.memo(() => { + console.debug("FeedsSourcesChart rendered!"); + + const chartProps = React.useMemo( + () => ({ + url: FEEDS_STATISTICS_SOURCES_URI, + accessorFnAggregation: (d) => d, + componentsFn: () => + Object.entries(FEED_COLOR_MAP) + .slice(0, 1) + .map(([dkey, color]) => ( + + )), + }), + [] + ); + + return ; +}); + +export const FeedsDownloadsChart = React.memo(() => { + console.debug("FeedsDownloadsChart rendered!"); + + const chartProps = React.useMemo( + () => ({ + url: FEEDS_STATISTICS_DOWNLOADS_URI, + accessorFnAggregation: (d) => d, + componentsFn: () => + Object.entries(FEED_COLOR_MAP) + .slice(1, 2) + .map(([dkey, color]) => ( + + )), + }), + [] + ); + + return ; +}); + +export const EnrichmentSourcesChart = React.memo(() => { + console.debug("EnrichmentSourcesChart rendered!"); + + const chartProps = React.useMemo( + () => ({ + url: ENRICHMENT_STATISTICS_SOURCES_URI, + accessorFnAggregation: (d) => d, + componentsFn: () => + Object.entries(ENRICHMENT_COLOR_MAP) + .slice(0, 1) + .map(([dkey, color]) => ( + + )), + }), + [] + ); + + return ; +}); + +export const EnrichmentRequestsChart = React.memo(() => { + console.debug("EnrichmentRequestsChart rendered!"); + + const chartProps = React.useMemo( + () => ({ + url: ENRICHMENT_STATISTICS_REQUESTS_URI, + accessorFnAggregation: (d) => d, + componentsFn: () => + Object.entries(ENRICHMENT_COLOR_MAP) + .slice(1, 2) + .map(([dkey, color]) => ( + + )), + }), + [] + ); + + return ; +}); + +export const FeedsTypesChart = React.memo(() => { + console.debug("FeedsTypesChart rendered!"); + + const chartProps = React.useMemo( + () => ({ + url: FEEDS_STATISTICS_TYPES_URI, + accessorFnAggregation: (d) => d, + componentsFn: (respData) => { + console.debug("respData", respData); + if (!respData || !respData?.length) return null; + + // Exctract keys only from respData[0]: + // feed types are the same for all elements of respData. + // Slice "date" field: we are only interested in feeds types. + const feedsTypes = []; + Object.entries(respData[0]) + .slice(1) + .map(([dKey], i) => (feedsTypes[i] = dKey)); + + // map each feed type to a color + return feedsTypes.map((dKey, i) => ( + + )); + }, + }), + [] + ); + + return ; +}); diff --git a/Submodules/GreedyBear/frontend/src/components/feeds/Feeds.jsx b/Submodules/GreedyBear/frontend/src/components/feeds/Feeds.jsx new file mode 100644 index 0000000..bb56a7f --- /dev/null +++ b/Submodules/GreedyBear/frontend/src/components/feeds/Feeds.jsx @@ -0,0 +1,232 @@ +import React from "react"; +import { Container, Button, Col, Label, FormGroup, Row } from "reactstrap"; +import { VscJson } from "react-icons/vsc"; +import { TbLicense } from "react-icons/tb"; +import { FEEDS_BASE_URI, GENERAL_HONEYPOT_URI } from "../../constants/api"; +import { + ContentSection, + Select, + useAxiosComponentLoader, + useDataTable, +} from "@certego/certego-ui"; +import { Form, Formik } from "formik"; +import { feedsTableColumns } from "./tableColumns"; +import { FEEDS_LICENSE } from "../../constants"; + +// costants +const feedTypeChoices = [ + { label: "All", value: "all" }, + { label: "Log4j", value: "log4j" }, + { label: "Cowrie", value: "cowrie" }, +]; + +const attackTypeChoices = [ + { label: "All", value: "all" }, + { label: "Scanner", value: "scanner" }, + { label: "Payload request", value: "payload_request" }, +]; + +const ageChoices = [ + { label: "Recent", value: "recent" }, + { label: "Persistent", value: "persistent" }, +]; + +const initialValues = { + feeds_type: "all", + attack_type: "all", + age: "recent", +}; + +const initialState = { + pageIndex: 0, +}; + +const toPassTableProps = { + columns: feedsTableColumns, + tableEmptyNode: ( + <> +

No Data

+ Note: Try changing filter. + + ), +}; + +let honeypotFeedsType = []; + +export default function Feeds() { + console.debug("Feeds rendered!"); + + console.debug("Feeds-initialValues", initialValues); + + const [url, setUrl] = React.useState( + `${FEEDS_BASE_URI}/${initialValues.feeds_type}/${initialValues.attack_type}/${initialValues.age}.json` + ); + + // API to extract general honeypot + const [honeypots, Loader] = useAxiosComponentLoader({ + url: `${GENERAL_HONEYPOT_URI}?onlyActive=true`, + headers: { "Content-Type": "application/json" }, + }); + console.debug("Feeds-honeypots:", honeypots); + + honeypots.forEach((honeypot) => { + //check if honeypot.label exist in honeypotFeedsType array or not (index === -1) + const index = honeypotFeedsType.findIndex((x) => x.label === honeypot); + if (index === -1) + honeypotFeedsType.push({ + label: honeypot, + value: honeypot.toLowerCase(), + }); + }); + + const [feedsData, tableNode, , tableStateReducer] = useDataTable( + { + url: FEEDS_BASE_URI, + params: { + feed_type: initialValues.feeds_type, + attack_type: initialValues.attack_type, + age: initialValues.age, + }, + initialParams: { + page: "1", + }, + }, + toPassTableProps, + (data) => data.results.iocs + ); + + // callbacks + const onSubmit = React.useCallback( + (values) => { + try { + setUrl( + `${FEEDS_BASE_URI}/${values.feeds_type}/${values.attack_type}/${values.age}.json` + ); + initialValues.feeds_type = values.feeds_type; + initialValues.attack_type = values.attack_type; + initialValues.age = values.age; + + const resetPage = { + type: "gotoPage", + pageIndex: 0, + }; + tableStateReducer(initialState, resetPage); + } catch (e) { + console.debug(e); + } + }, + [setUrl, tableStateReducer] + ); + + return ( + +
+

+ Feeds  + {feedsData?.count} total +

+ +
+ + + + {/* Form */} + ( + + {(formik) => ( +
+ + + + { + formik.handleChange(e); + formik.submitForm(); + }} + /> + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

GreedyBear

+

GreedyBear

+

GitHub release (latest by date) +GitHub Repo stars +Twitter Follow +Linkedin

+

CodeFactor +Code style: black +Imports: isort +CodeQL +Dependency Review +Pull request automation

+

The project goal is to extract data of the attacks detected by a TPOT or a cluster of them and to generate some feeds that can be used to prevent and detect attacks.

+

Official announcement here.

+

Documentation

+

Documentation about GreedyBear installation, usage, configuration and contribution can be found at this link

+

Public feeds

+

There are public feeds provided by The Honeynet Project in this site. Example

+

Please do not perform too many requests to extract feeds or you will be banned.

+

If you want to be updated regularly, please download the feeds only once every 10 minutes (this is the time between each internal update).

+

To check all the available feeds, Please refer to our usage guide

+

Enrichment Service

+

GreedyBear provides an easy-to-query API to get the information available in GB regarding the queried observable (domain or IP address).

+

To understand more, Please refer to our usage guide

+

Run Greedybear on your environment

+

The tool has been created not only to provide the feeds from The Honeynet Project's cluster of TPOTs.

+

If you manage one or more T-POTs of your own, you can get the code of this application and run Greedybear on your environment. +In this way, you are able to provide new feeds of your own.

+

To install it locally, Please refer to our installation guide

+

Sponsors

+

Certego

+

Certego Logo

+

Certego is a MDR (Managed Detection and Response) and Threat Intelligence Provider based in Italy.

+

Started as a personal Christmas project from Matteo Lodi, since then GreedyBear is being improved mainly thanks to the efforts of the Certego Threat Intelligence Team.

+

The Honeynet Project

+

Honeynet.org logo

+

The Honeynet Project is a non-profit organization working on creating open source cyber security tools and sharing knowledge about cyber threats.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/GreedyBear/installer_on_tpot.sh b/Submodules/GreedyBear/installer_on_tpot.sh new file mode 100644 index 0000000..fdb8f67 --- /dev/null +++ b/Submodules/GreedyBear/installer_on_tpot.sh @@ -0,0 +1,484 @@ +#!/bin/bash +# Greedybear on TPot Instance Installer + +# Code based on https://github.com/telekom-security/tpotce/blob/master/iso/installer/install.sh + +################## +# I. Global vars # +################## + +myBACKTITLE="GreedyBear-on-TPot-Installer" +myREMOTESITES="https://github.com https://hub.docker.com" +myPROGRESSBOXCONF=" --backtitle "$myBACKTITLE" --progressbox 24 80" + +myINFO="\ +############################################# +### Greedybear Installer on TPot Instance ### +############################################# +Disclaimer: +This script will install Greedybear on this +system, assuming an existing TPot instance. +############################################# +Usage: + $0 --help - Help. +" + +myGREEDYBEAR_PORT=8008 # greedybear website +myGREEDYBEAR_PORT2=8000 # in case of type='https' - this is the http port +myELASTIC_PORT=64298 + +myGREEDYBEAR_USER="greedybear" +myGREEDYBEAR_PASS="greedybear" + +myFOLDER=/opt/GreedyBear +INSTALLED=false + +myTYPE="http" + +NETWORKS="\ + +networks: + default: + name: etc_default + external: true +" + +SUPERUSER_CREATION=' +if [ "$DJANGO_SUPERUSER_USERNAME" ] +then + python3 manage.py createsuperuser \ + --noinput \ + --username $DJANGO_SUPERUSER_USERNAME \ + --email $DJANGO_SUPERUSER_EMAIL \ + --first_name $DJANGO_SUPERUSER_FIRST_NAME \ + --last_name $DJANGO_SUPERUSER_LAST_NAME +fi +' + +GREEDYBEAR_SERVICE="\ +[Unit] +Description=greedybear +Requires=docker.service +After=docker.service +Requires=tpot.service +After=tpot.service + +[Service] +Restart=always +RestartSec=5 +TimeoutSec=infinity + +# Compose Greedybear up +ExecStart=/bin/bash -c 'cd /opt/GreedyBear && /usr/bin/docker-compose up' + +# Compose Greedybear down, remove containers and volumes +ExecStop=/bin/bash -c 'cd /opt/GreedyBear && /usr/bin/docker-compose down' + +[Install] +WantedBy=multi-user.target +" + +emailREGEX="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$" + + +################# +# II. Functions # +################# + +# Create banners +function fuBANNER { + toilet -f ivrit "$1" +} + +# Do we have root? +function fuGOT_ROOT { + echo + echo -n "### Checking for root: " + if [ "$(whoami)" != "root" ]; + then + echo "[ NOT OK ]" + echo "### Please run as root." + echo "### Example: sudo $0" + exit + else + echo "[ OK ]" + fi +} + +# Check for other services +function fuCHECK_PORTS_GREEDYBEAR { + echo + echo "### Checking for active services." + echo + grc netstat -tulpen + echo + echo "### Please review your running services." + echo "### Port $myGREEDYBEAR_PORT should be free for the Greedybear Website." + if [ "$myTYPE" == "https" ]; + then + echo "### Port $myGREEDYBEAR_PORT2 should be free for the Greedybear HTTP Website." + fi + echo "### Port $myELASTIC_PORT should be the TPot Elasticsearch instance." + echo + while [ 1 != 2 ] + do + read -s -n 1 -p "Continue [y/n]? " mySELECT + echo + case "$mySELECT" in + [y,Y]) + break + ;; + [n,N]) + exit + ;; + esac + done +} + +# Check if remote sites are available +function fuCHECKNET_GB { + local myREMOTESITES="$1" + mySITESCOUNT=$(echo $mySITES | wc -w) + j=0 + for i in $myREMOTESITES; + do + echo $(expr 100 \* $j / $mySITESCOUNT) | dialog --title "[ Availability check ]" --backtitle "$myBACKTITLE" --gauge "\n Now checking: $i\n" 8 80 + curl --connect-timeout 30 -IsS $i 2>&1>/dev/null + if [ $? -ne 0 ]; + then + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Continue? ]" --yesno "\nAvailability check failed. You can continue, but the installation might fail." 10 50 + if [ $? = 1 ]; + then + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Abort ]" --msgbox "\nInstallation aborted. Exiting the installer." 7 50 + exit + else + break; + fi; + fi; + let j+=1 + echo $(expr 100 \* $j / $mySITESCOUNT) | dialog --keep-window --title "[ Availability check ]" --backtitle "$myBACKTITLE" --gauge "\n Now checking: $i\n" 8 80 + done; +} + +############################ +# III. Pre-Installer phase # +############################ + +fuGOT_ROOT + +###################################### +# IV. Prepare installer environment # +###################################### + +for i in "$@" + do + case $i in + --port=*) + myGREEDYBEAR_PORT="${i#*=}" + shift + ;; + --type=http) + myTYPE="${i#*=}" + shift + ;; + --type=https) + myTYPE="${i#*=}" + shift + ;; + --type=local) + myTYPE="${i#*=}" + shift + ;; + --folder=*) + myFOLDER="${i#*=}" + INSTALLED=true + shift + ;; + --help) + echo " $0 " + echo + echo "--type=<[http, https, local]>" + echo " choose Compose Files" + echo " Plain HTTP (default), production with HTTPS enabled or local development" + echo + echo "--folder=" + echo " absolute path to already installed GreedyBear (optional)" + echo " default: assumes GreedyBear is not installed - installs in '\opt\GreedyBear'" + echo + exit + ;; + *) + echo "$myINFO" + exit + ;; + esac + done + +# Check if remote sites are available +fuCHECKNET_GB "$myREMOTESITES" + +####################################### +# V. Installer user interaction phase # +####################################### + +# Possible changes in Ports for Geedybear website +while [ 1 != 2 ] + do + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Greedybear Port ]" --defaultno --yesno "\nDo you want to change the default Greedybear Web Port ($myGREEDYBEAR_PORT)" 7 50 + myOK=$? + echo + if [ "$myOK" == "0" ]; + then + myPORT=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Greedybear Port ]" --inputbox "\nEnter Port:" 8 40 3>&1 1>&2 2>&3 3>&-) + if [[ $myPORT =~ ^[0-9]+$ ]] && [ $myPORT -ge 0 ] && [ $myPORT -le 65535 ]; + then + myGREEDYBEAR_PORT=$myPORT + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Port Accepted. ]" \ + --msgbox "\nGreedybear's website will be deployed on Port $myGREEDYBEAR_PORT." 7 60 + break + else + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Port Invalid. ]" \ + --msgbox "\nPlease re-enter Port." 7 60 + fi + else + break + fi +done + +# If HTTPS need second port for HTTP redirect (cannot be 80 because of potential Honeypots) +if [ "$myTYPE" == "https" ]; + then + while [ 1 != 2 ] + do + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Greedybear (HTTP) Port ]" --defaultno --yesno "\nDo you want to change the default Greedybear (HTTP) Web Port ($myGREEDYBEAR_PORT2)" 7 50 + myOK=$? + echo + if [ "$myOK" == "0" ]; + then + myPORT=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Greedybear (HTTP) Port ]" --inputbox "\nEnter Port:" 8 40 3>&1 1>&2 2>&3 3>&-) + if [[ $myPORT =~ ^[0-9]+$ ]] && [ $myPORT -ge 0 ] && [ $myPORT -le 65535 ]; + then + myGREEDYBEAR_PORT2=$myPORT + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Port Accepted. ]" \ + --msgbox "\nGreedybear's (HTTP) Port will be $myGREEDYBEAR_PORT2." 7 60 + break + else + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Port Invalid. ]" \ + --msgbox "\nPlease re-enter Port." 7 60 + fi + else + break + fi + done +fi + + +# If TPot Elasticsearch Port (internal Docker Network) was changed +while [ 1 != 2 ] + do + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Elasticsearch Port ]" --defaultno --yesno "\nDid you change the default Elasticsearch Port ($myELASTIC_PORT)" 7 50 + myOK=$? + echo + if [ "$myOK" == "0" ]; + then + myPORT=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Elasticsearch Port ]" --inputbox "\nEnter Port:" 8 40 3>&1 1>&2 2>&3 3>&-) + if [[ $myPORT =~ ^[0-9]+$ ]] && [ $myPORT -ge 0 ] && [ $myPORT -le 65535 ]; + then + myELASTIC_PORT=$myPORT + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Port Accepted. ]" \ + --msgbox "\nElasticsearch Port will be $myELASTIC_PORT." 7 60 + break + else + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Port Invalid. ]" \ + --msgbox "\nPlease re-enter Port." 7 60 + fi + else + break + fi +done + +dialog --clear + +fuCHECK_PORTS_GREEDYBEAR + +# SuperUser Creds for Web UWSGI/Django +myUSERNAME=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Web Username ]" --inputbox "\nEnter Username:" 8 40 3>&1 1>&2 2>&3 3>&-) +if [ -z "$myUSERNAME" ]; + then + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Web Username ]" \ + --msgbox "\nUsing default username \"$myGREEDYBEAR_USER\"" 7 60 + else + myGREEDYBEAR_USER=$myUSERNAME +fi +myPASS1="pass1" +myPASS2="pass2" +mySECURE="0" +while [ "$myPASS1" != "$myPASS2" ] && [ "$mySECURE" == "0" ] + do + while [ "$myPASS1" == "pass1" ] || [ "$myPASS1" == "" ] + do + myPASS1=$(dialog --keep-window --insecure --backtitle "$myBACKTITLE" \ + --title "[ Enter password for Greedybear Web User ]" \ + --passwordbox "\nPassword" 9 60 3>&1 1>&2 2>&3 3>&-) + done + myPASS2=$(dialog --keep-window --insecure --backtitle "$myBACKTITLE" \ + --title "[ Enter password again for Greedybear Web User ]" \ + --passwordbox "\nPassword" 9 60 3>&1 1>&2 2>&3 3>&-) + if [ "$myPASS1" != "$myPASS2" ]; + then + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Passwords do not match. ]" \ + --msgbox "\nPlease re-enter your password." 7 60 + myPASS1="pass1" + myPASS2="pass2" + fi + mySECURE=$(printf "%s" "$myPASS1" | cracklib-check | grep -c "OK") + if [ "$mySECURE" == "0" ] && [ "$myPASS1" == "$myPASS2" ]; + then + dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Password is not secure ]" --defaultno --yesno "\nKeep insecure password?" 7 50 + myOK=$? + if [ "$myOK" == "1" ]; + then + myPASS1="pass1" + myPASS2="pass2" + fi + fi + done +myGREEDYBEAR_PASS=$myPASS1 + +# additional info for automatic creation of superuser +myGREEDYBEAR_EMAIL="" +myGREEDYBEAR_LAST_NAME="" +myGREEDYBEAR_FIRST_NAME="" +myGREEDYBEAR_EMAIL=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Web User ]" --inputbox "\nEnter an Email for the Greedybear Web User (optional):" 8 40 3>&1 1>&2 2>&3 3>&-) +myGREEDYBEAR_FIRST_NAME=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Web User ]" --inputbox "\nEnter an First Name for the Greedybear Web User (optional):" 8 40 3>&1 1>&2 2>&3 3>&-) +myGREEDYBEAR_LAST_NAME=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Web User ]" --inputbox "\nEnter an Last Name for the Greedybear Web User (optional):" 8 40 3>&1 1>&2 2>&3 3>&-) + +echo + +# Slack Info +mySLACK_TOKEN="" +mySLACK_CHANNEL="" +mySLACK_TOKEN=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Slack ]" --inputbox "\nEnter a Slack Token (optional):" 8 40 3>&1 1>&2 2>&3 3>&-) +mySLACK_CHANNEL=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Slack ]" --inputbox "\nEnter a Slack Channel (optional):" 8 40 3>&1 1>&2 2>&3 3>&-) + +# Django Secret +myDJANGO_SECRET="" +myDJANGO_SECRET=$(dialog --keep-window --backtitle "$myBACKTITLE" --title "[ Django ]" --inputbox "\nEnter Django Secret (optional):" 8 40 3>&1 1>&2 2>&3 3>&-) + +dialog --clear + +########################## +# VI. Installation phase # +########################## + +exec 2> >(tee "/greedybear_install.err") +exec > >(tee "/greedybear_install.log") + +fuBANNER "Installing ..." + +# Cloning Greedybear from GitHub +fuBANNER "Cloning Greedybear ..." +if [ "$INSTALLED" = false ] ; then + git clone https://github.com/intelowlproject/GreedyBear $myFOLDER +fi + +# Set Configs +fuBANNER "Copying config files ..." +cp $myFOLDER/.env_template $myFOLDER/.env +cp $myFOLDER/docker/env_file_template $myFOLDER/docker/env_file +cp $myFOLDER/docker/env_file_postgres_template $myFOLDER/docker/env_file_postgres + +# Write changes to config files +fuBANNER "Change config files ..." + +# .env - set what docker config files are used +if [ "$myTYPE" != "http" ]; + then + #default + sed -i '/^COMPOSE_FILE=docker\/default.yml$/ s/./#&/' $myFOLDER/.env +fi +if [ "$myTYPE" != "https" ]; + then + #https + sed -i '/^COMPOSE_FILE=docker\/default.yml:docker\/https.override.yml$/ s/./#&/' $myFOLDER/.env +fi +if [ "$myTYPE" != "local" ]; + then + #local + sed -i '/^COMPOSE_FILE=docker\/default.yml:docker\/local.override.yml$/ s/./#&/' $myFOLDER/.env +fi + +#reduce version of docker compose files to work with docker-compose version from the Tpot instance +sed -i "s/version: '3.8'/version: '3.5'/g" $myFOLDER/docker/default.yml +sed -i "s/version: '3.8'/version: '3.5'/g" $myFOLDER/docker/https.override.yml +sed -i "s/version: '3.8'/version: '3.5'/g" $myFOLDER/docker/local.override.yml + +# env_file - set elasticendpoint, django secret+user+pass and slack info +sed -i "/ELASTIC_ENDPOINT=/ s/$/http:\/\/elasticsearch:$myELASTIC_PORT/" $myFOLDER/docker/env_file + +sed -i "/DJANGO_SECRET=/ s/$/$myDJANGO_SECRET/" $myFOLDER/docker/env_file + +sed -i "/SLACK_TOKEN=/ s/$/$mySLACK_TOKEN/" $myFOLDER/docker/env_file +sed -i "/SLACK_CHANNEL=/ s/$/$mySLACK_CHANNEL/" $myFOLDER/docker/env_file + +# add django superuser secrets +echo "" >> $myFOLDER/docker/env_file +echo "DJANGO_SUPERUSER_USERNAME=$myGREEDYBEAR_USER" >> $myFOLDER/docker/env_file +echo "DJANGO_SUPERUSER_PASSWORD=$myGREEDYBEAR_PASS" >> $myFOLDER/docker/env_file +echo "DJANGO_SUPERUSER_EMAIL=\'$myGREEDYBEAR_EMAIL\'" >> $myFOLDER/docker/env_file +echo "DJANGO_SUPERUSER_FIRST_NAME=\'$myGREEDYBEAR_FIRST_NAME\'" >> $myFOLDER/docker/env_file +echo "DJANGO_SUPERUSER_LAST_NAME=\'$myGREEDYBEAR_LAST_NAME\'" >> $myFOLDER/docker/env_file + +# run superuser create comment when uwsgi starts +echo "$SUPERUSER_CREATION" >> $myFOLDER/docker/entrypoint_uwsgi.sh + +# docker/default.yml set greedybear port + put in same network as TPot instance +if [ "$myTYPE" != "https" ]; + then + sed -i -e "s/80:80/$myGREEDYBEAR_PORT:80/g" $myFOLDER/docker/default.yml +fi + +echo "$NETWORKS" >> $myFOLDER/docker/default.yml + +# docker/https.override.yml set greedybear port + SSL +if [ "$myTYPE" == "https" ]; + then + sed -i "s/443:443/$myGREEDYBEAR_PORT:443/g" $myFOLDER/docker/https.override.yml + sed -i -e "s/80:80/$myGREEDYBEAR_PORT2:80/g" $myFOLDER/docker/default.yml + + #TODO write user in ssl_password/combine with TPot cert? + fuBANNER "Webuser creds" + #htpasswd -b -c /etc/ssl/private/ssl_passwords.txt "$myU" "$myP" + touch /etc/ssl/private/ssl_passwords.txt + echo + + fuBANNER "NGINX Certificate" + myINTIP=$(hostname -I | awk '{ print $1 }') + mkdir -p /data/nginx/cert + openssl req \ + -nodes \ + -x509 \ + -sha512 \ + -newkey rsa:8192 \ + -keyout "/etc/ssl/private/greedybear.key" \ + -out "/usr/local/share/ca-certificates/greedybear.crt" \ + -days 3650 \ + -subj '/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd' \ + -addext "subjectAltName = IP:$myINTIP" +fi + +# BUILD Docker Container - files depending on choosen Type +fuBANNER "Building Containers ..." + +cd $myFOLDER +/usr/bin/docker-compose build + +# Create Systemctl Service +fuBANNER "Creating Service ..." +echo "$GREEDYBEAR_SERVICE" > /etc/systemd/system/greedybear.service +sed -i "s@\/opt\/GreedyBear@$myFOLDER@g" /etc/systemd/system/greedybear.service +# Enable+Start Systemctl Service +systemctl daemon-reload +systemctl enable greedybear.service +systemctl start greedybear.service +systemctl status greedybear.service \ No newline at end of file diff --git a/Submodules/GreedyBear/manage.py b/Submodules/GreedyBear/manage.py new file mode 100644 index 0000000..1d24ff4 --- /dev/null +++ b/Submodules/GreedyBear/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + + +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "greedybear.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/Submodules/GreedyBear/requirements/django-server-requirements.txt b/Submodules/GreedyBear/requirements/django-server-requirements.txt new file mode 100644 index 0000000..fa2160f --- /dev/null +++ b/Submodules/GreedyBear/requirements/django-server-requirements.txt @@ -0,0 +1,2 @@ +# to allow Watchman to be used with Django Development Server +pywatchman==2.0.0 \ No newline at end of file diff --git a/Submodules/GreedyBear/requirements/project-requirements.txt b/Submodules/GreedyBear/requirements/project-requirements.txt new file mode 100644 index 0000000..22433af --- /dev/null +++ b/Submodules/GreedyBear/requirements/project-requirements.txt @@ -0,0 +1,16 @@ +celery==5.3.6 +elasticsearch-dsl==7.4.1 + +Django==4.2.15 +djangorestframework==3.15.2 +django-rest-email-auth==4.0.3 +django-ses==3.5.0 + +drf-spectacular==0.26.2 +psycopg2-binary==2.9.9 + +certego-saas==0.7.10 +slack-sdk==3.31.0 + +uwsgitop==0.12 +uwsgi==2.0.24 \ No newline at end of file diff --git a/Submodules/GreedyBear/static/Certego.png b/Submodules/GreedyBear/static/Certego.png new file mode 100644 index 0000000..82d095b Binary files /dev/null and b/Submodules/GreedyBear/static/Certego.png differ diff --git a/Submodules/GreedyBear/static/greedybear.png b/Submodules/GreedyBear/static/greedybear.png new file mode 100644 index 0000000..887aada Binary files /dev/null and b/Submodules/GreedyBear/static/greedybear.png differ diff --git a/Submodules/GreedyBear/static/honeynet_logo.png b/Submodules/GreedyBear/static/honeynet_logo.png new file mode 100644 index 0000000..b6e5f96 Binary files /dev/null and b/Submodules/GreedyBear/static/honeynet_logo.png differ diff --git a/Submodules/GreedyBear/tests/__init__.py b/Submodules/GreedyBear/tests/__init__.py new file mode 100644 index 0000000..445f84c --- /dev/null +++ b/Submodules/GreedyBear/tests/__init__.py @@ -0,0 +1,45 @@ +from datetime import datetime + +from certego_saas.apps.user.models import User +from django.test import TestCase +from greedybear.models import IOC, GeneralHoneypot, iocType + + +class CustomTestCase(TestCase): + @classmethod + def setUpTestData(cls): + super(CustomTestCase, cls).setUpTestData() + + cls.heralding = GeneralHoneypot.objects.create(name="Heralding", active=True) + cls.ciscoasa = GeneralHoneypot.objects.create(name="Ciscoasa", active=True) + + cls.current_time = datetime.utcnow() + cls.ioc = IOC.objects.create( + name="140.246.171.141", + type=iocType.IP.value, + first_seen=cls.current_time, + last_seen=cls.current_time, + days_seen=[cls.current_time], + number_of_days_seen=1, + times_seen=1, + log4j=True, + cowrie=True, + scanner=True, + payload_request=True, + related_urls=[], + ) + + cls.ioc.general_honeypot.add(cls.heralding) # FEEDS + cls.ioc.general_honeypot.add(cls.ciscoasa) # FEEDS + cls.ioc.save() + + try: + cls.superuser = User.objects.get(is_superuser=True) + except User.DoesNotExist: + cls.superuser = User.objects.create_superuser(username="test", email="test@greedybear.com", password="test") + + @classmethod + def tearDownClass(self): + # db clean + GeneralHoneypot.objects.all().delete() + IOC.objects.all().delete() diff --git a/Submodules/GreedyBear/tests/authentication/__init__.py b/Submodules/GreedyBear/tests/authentication/__init__.py new file mode 100644 index 0000000..b2d6f38 --- /dev/null +++ b/Submodules/GreedyBear/tests/authentication/__init__.py @@ -0,0 +1,29 @@ +from django.contrib.auth import get_user_model +from rest_framework.test import APIClient, APITestCase + +User = get_user_model() + + +class CustomOAuthTestCase(APITestCase): + @classmethod + def setUpClass(cls): + # test data + username = "john.doe" + email = "john.doe@example.com" + password = "hunter2" + if User.objects.filter(username=username).exists(): + user = User.objects.get(username=username) + user.delete() + cls.user = User.objects.create_superuser(username, email, password) + cls.creds = { + "username": username, + "password": password, + } + # setup client + cls.client = APIClient() + return super().setUpClass() + + @classmethod + def tearDownClass(cls): + cls.user.delete() + return super().tearDownClass() diff --git a/Submodules/GreedyBear/tests/authentication/test_auth.py b/Submodules/GreedyBear/tests/authentication/test_auth.py new file mode 100644 index 0000000..26bed2a --- /dev/null +++ b/Submodules/GreedyBear/tests/authentication/test_auth.py @@ -0,0 +1,364 @@ +from django.contrib.auth import get_user_model +from django.core import mail +from django.core.cache import cache +from django.test import tag +from durin.models import AuthToken, Client +from rest_email_auth.models import EmailConfirmation, PasswordResetToken +from rest_framework.reverse import reverse + +from . import CustomOAuthTestCase + +User = get_user_model() +login_uri = reverse("auth_login") +logout_uri = reverse("auth_logout") +register_uri = reverse("auth_register") +verify_email_uri = reverse("auth_verify-email") +resend_verificaton_uri = reverse("auth_resend-verification") +request_pwd_reset_uri = reverse("auth_request-password-reset") +reset_pwd_uri = reverse("auth_reset-password") + + +@tag("api", "user") +class TestUserAuth(CustomOAuthTestCase): + def setUp(self): + # test data + self.testregisteruser = { + "email": "testregisteruser@test.com", + "username": "testregisteruser", + "first_name": "testregisteruser", + "last_name": "testregisteruser", + "password": "testregisteruser", + "profile": { + "company_name": "companytest", + "company_role": "greedybear test", + "twitter_handle": "@fake", + "discover_from": "other", + }, + } + mail.outbox = [] + + def tearDown(self): # skipcq: PYL-R0201 + # cache clear (for throttling) + cache.clear() + # db clean + AuthToken.objects.all().delete() + Client.objects.all().delete() + + def test_login_200(self): + self.assertEqual(AuthToken.objects.count(), 0) + body = { + **self.creds, + } + + response = self.client.post(login_uri, body) + cookies_data = response.cookies + msg = (response, cookies_data) + + self.assertEqual(response.status_code, 200, msg=msg) + self.assertIn("CERTEGO_SAAS_AUTH_TOKEN", cookies_data, msg=msg) + + self.assertEqual(AuthToken.objects.count(), 1) + + def test_logout_204(self): + self.assertEqual(AuthToken.objects.count(), 0) + + token = AuthToken.objects.create( + user=self.user, + client=Client.objects.create(name="test_logout_deletes_keys"), + ) + self.assertEqual(AuthToken.objects.count(), 1) + + self.client.credentials(HTTP_AUTHORIZATION=("Token %s" % token.token)) + response = self.client.post(logout_uri) + + self.assertEqual(response.status_code, 204, msg=(response)) + self.assertEqual(AuthToken.objects.count(), 0, "other tokens should remain after logout") + + def test_register_username_taken_400(self): + self.assertEqual(User.objects.count(), 1) + + body = { + **self.creds, + "first_name": "blahblah", + "last_name": "blahblah", + "email": self.testregisteruser["email"], + } + + response = self.client.post(register_uri, body) + content = response.json() + msg = ( + response, + content, + "self.user already exists so unique username validation will fail.", + ) + + # response assertions + self.assertEqual(400, response.status_code, msg=msg) + self.assertIn( + "A user with that username already exists.", + content["errors"]["username"], + msg=msg, + ) + # db assertions + self.assertEqual(User.objects.count(), 1, msg="no new user was created") + + def test_register_no_email_leak_201(self): + self.assertEqual(User.objects.count(), 1) + + # base check + with self.assertRaises(User.DoesNotExist, msg="testregisteruser doesn't exist right now"): + User.objects.get(username=self.testregisteruser["username"]) + + # register new user + self.__register_user(body=self.testregisteruser) + self.assertEqual(User.objects.count(), 2) + + # 2nd registration for same email returns 201 + # only as to not leak registered emails + body = { + "email": self.testregisteruser["email"], + "profile": self.testregisteruser["profile"], + "username": "blahblah", + "first_name": "blahblah", + "last_name": "blahblah", + "password": "averystrongpassword", + } + self.__register_user(body=body) + + # db assertions + self.assertEqual(User.objects.count(), 2, msg="no new user was created") + + def test_register_201(self): + self.assertEqual(User.objects.count(), 1) + + with self.assertRaises(User.DoesNotExist, msg="testregisteruser doesn't exist right now"): + User.objects.get(username=self.testregisteruser["username"]) + + # test + self.__register_user(body=self.testregisteruser) + + # db assertions + user = User.objects.get(username=self.testregisteruser["username"]) + self.assertEqual(User.objects.count(), 2) + self.assertFalse(user.is_active, msg="newly registered user must have is_active=False") + + def test_verify_email_200(self): + # register new user + self.__register_user(body=self.testregisteruser) + + # db assertions + user = User.objects.get(username=self.testregisteruser["username"]) + self.assertFalse(user.is_active, msg="newly registered user must have is_active=False") + + # get EmailConfirmation instance that was created after registration + email_confirmation_obj = EmailConfirmation.objects.get(email=user.email_addresses.first()) + + # send verify email request + response = self.client.post(verify_email_uri, {"key": email_confirmation_obj.key}) + + content = response.json() + msg = (response, content, "User should now be verified") + + # email assertions + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].subject, "GreedyBear - Please Verify Your Email Address") + self.assertEqual(mail.outbox[0].to[0], "testregisteruser@test.com") + + # response assertions + self.assertEqual(200, response.status_code, msg=msg) + + # db assertions + user.refresh_from_db() + self.assertFalse(user.is_active, msg="even after verification is_active must be False") + + def test_resend_verification_email_200(self): + # register new user + # send first verify email request + self.__register_user(body=self.testregisteruser) + + # request second verification email + response = self.client.post( + resend_verificaton_uri, + { + "email": self.testregisteruser["email"], + }, + ) + content = response.json() + msg = (response, content) + + # email assertions + self.assertEqual(len(mail.outbox), 2) + self.assertEqual(mail.outbox[0].subject, "GreedyBear - Please Verify Your Email Address") + self.assertEqual(mail.outbox[0].to[0], "testregisteruser@test.com") + self.assertEqual(mail.outbox[1].subject, "GreedyBear - Please Verify Your Email Address") + self.assertEqual(mail.outbox[1].to[0], "testregisteruser@test.com") + + self.assertEqual(200, response.status_code, msg=msg) + self.assertEqual(self.testregisteruser["email"], content["email"], msg=msg) + + def test_password_reset_flow_200(self): + # register new user + self.__register_user(body=self.testregisteruser) + user = User.objects.get(username=self.testregisteruser["username"]) + email_obj = user.email_addresses.first() + email_obj.is_verified = True # cant request pwd reset if email not verified + email_obj.save() + + # step 1: request password reset email + response = self.client.post( + request_pwd_reset_uri, + { + "email": self.testregisteruser["email"], + }, + ) + content = response.json() + msg = (response, content) + + self.assertEqual(200, response.status_code, msg=msg) + self.assertEqual(self.testregisteruser["email"], content["email"], msg=msg) + + pwd_reset_obj = PasswordResetToken.objects.get(email=email_obj) + new_password = "new_password_for_test_1234" + + # step 2: reset-password submission + response = self.client.post( + reset_pwd_uri, + { + "key": pwd_reset_obj.key, + "password": new_password, + }, + ) + content = response.json() + msg = (response, content, "check_password should return True") + + self.assertEqual(200, response.status_code, msg=msg) + user.refresh_from_db() + self.assertTrue(user.check_password(new_password), msg=msg) + + def test_min_password_lenght_400(self): + self.assertEqual(User.objects.count(), 1) + + # register new user with invalid password + body = { + **self.creds, + "email": self.testregisteruser["email"], + "username": "blahblah", + "first_name": "blahblah", + "last_name": "blahblah", + "password": "greedybear", + } + + response = self.client.post(register_uri, body) + content = response.json() + + # response assertions + self.assertEqual(400, response.status_code) + self.assertIn( + "Invalid password", + content["errors"]["password"], + ) + + # db assertions + self.assertEqual(User.objects.count(), 1, msg="no new user was created") + + def test_special_characters_password_400(self): + self.assertEqual(User.objects.count(), 1) + + # register new user with invalid password + body = { + **self.creds, + "email": self.testregisteruser["email"], + "username": "blahblah", + "first_name": "blahblah", + "last_name": "blahblah", + "password": "greedybeargreedybear$", + } + + response = self.client.post(register_uri, body) + content = response.json() + + # response assertions + self.assertEqual(400, response.status_code) + self.assertIn( + "Invalid password", + content["errors"]["password"], + ) + + # db assertions + self.assertEqual(User.objects.count(), 1, msg="no new user was created") + + # utils + def __register_user(self, body: dict): + response = self.client.post(register_uri, {**body}, format="json") + content = response.json() + msg = (response, content) + + # response assertions + self.assertEqual(201, response.status_code, msg=msg) + self.assertEqual(content["username"], body["username"], msg=msg) + self.assertEqual(content["email"], body["email"], msg=msg) + self.assertFalse(content["is_active"], msg="newly registered user must have is_active=False") + + +class CheckConfigurationTestCase(CustomOAuthTestCase): + def test_200_local_setup(self): + with self.settings(DEFAULT_FROM_EMAIL="fake@email.it", DEFAULT_EMAIL="fake@email.it", STAGE_LOCAL="true"): + response = self.client.get("/api/auth/configuration?page=register") + self.assertEqual(response.status_code, 200) + response = self.client.get("/api/auth/configuration?page=login") + self.assertEqual(response.status_code, 200) + + def test_200_prod_smtp_setup(self): + with self.settings( + DEFAULT_FROM_EMAIL="fake@email.it", + DEFAULT_EMAIL="fake@email.it", + STAGE_PRODUCTION="true", + EMAIL_HOST="test", + EMAIL_HOST_USER="test", + EMAIL_HOST_PASSWORD="test", + EMAIL_PORT="test", + ): + response = self.client.get("/api/auth/configuration?page=register") + self.assertEqual(response.status_code, 200) + + def test_200_prod_ses_setup(self): + with self.settings( + DEFAULT_FROM_EMAIL="fake@email.it", + DEFAULT_EMAIL="fake@email.it", + STAGE_PRODUCTION="true", + AWS_SES="true", + AWS_ACCESS_KEY_ID="test", + AWS_SECRET_ACCESS_KEY="test", + ): + response = self.client.get("/api/auth/configuration?page=register") + self.assertEqual(response.status_code, 200) + + def test_501_local_setup(self): + with self.settings(DEFAULT_FROM_EMAIL="", DEFAULT_EMAIL="", STAGE_LOCAL="true"): + response = self.client.get("/api/auth/configuration?page=register") + self.assertEqual(response.status_code, 501) + + def test_501_prod_smtp_setup(self): + with self.settings( + DEFAULT_FROM_EMAIL="fake@email.it", + DEFAULT_EMAIL="fake@email.it", + STAGE_PRODUCTION="true", + EMAIL_HOST="", + EMAIL_HOST_USER="", + EMAIL_HOST_PASSWORD="", + EMAIL_PORT="", + ): + response = self.client.get("/api/auth/configuration?page=register") + self.assertEqual(response.status_code, 501) + + def test_501_prod_ses_setup(self): + with self.settings( + DEFAULT_FROM_EMAIL="fake@email.it", + DEFAULT_EMAIL="fake@email.it", + STAGE_PRODUCTION="true", + AWS_SES="true", + AWS_ACCESS_KEY_ID="", + AWS_SECRET_ACCESS_KEY="", + ): + response = self.client.get("/api/auth/configuration?page=register") + self.assertEqual(response.status_code, 501) diff --git a/Submodules/GreedyBear/tests/greedybear/__init__.py b/Submodules/GreedyBear/tests/greedybear/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/GreedyBear/tests/greedybear/cronjobs/__init__.py b/Submodules/GreedyBear/tests/greedybear/cronjobs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_cowrie.py b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_cowrie.py new file mode 100644 index 0000000..cb6b780 --- /dev/null +++ b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_cowrie.py @@ -0,0 +1,16 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. + +from unittest import TestCase + +from greedybear.cronjobs import cowrie +from greedybear.models import IOC + + +class CowrieTestCase(TestCase): + def test_sensors(self, *args, **kwargs): + a = cowrie.ExtractCowrie() + a.execute() + self.assertTrue(a.success) + iocs = IOC.objects.filter(cowrie=True) + self.assertTrue(iocs) diff --git a/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_general.py b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_general.py new file mode 100644 index 0000000..74bb524 --- /dev/null +++ b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_general.py @@ -0,0 +1,21 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. + +from unittest import TestCase + +from django.db.models import Q +from greedybear.cronjobs import general +from greedybear.models import IOC + +# FEEDS + + +class GeneralTestCase(TestCase): + def test_sensors(self, *args, **kwargs): + a = general.ExtractGeneral() + a.execute() + self.assertTrue(a.success) + iocs = [] + for hp in ["heralding", "ciscoasa"]: + iocs.extend(IOC.objects.filter(Q(general_honeypot__name__iexact=hp))) + self.assertTrue(iocs) diff --git a/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_log4pot.py b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_log4pot.py new file mode 100644 index 0000000..7ff4db5 --- /dev/null +++ b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_log4pot.py @@ -0,0 +1,16 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. + +from unittest import TestCase + +from greedybear.cronjobs import log4pot +from greedybear.models import IOC + + +class Log4PotTestCase(TestCase): + def test_sensors(self, *args, **kwargs): + a = log4pot.ExtractLog4Pot() + a.execute() + self.assertTrue(a.success) + iocs = IOC.objects.filter(log4j=True) + self.assertTrue(iocs) diff --git a/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_sensors.py b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_sensors.py new file mode 100644 index 0000000..b5386ab --- /dev/null +++ b/Submodules/GreedyBear/tests/greedybear/cronjobs/only_manual/test_sensors.py @@ -0,0 +1,16 @@ +# This file is a part of GreedyBear https://github.com/honeynet/GreedyBear +# See the file 'LICENSE' for copying permission. + +from unittest import TestCase + +from greedybear.cronjobs import sensors +from greedybear.models import Sensors + + +class SensorsTestCase(TestCase): + def test_sensors(self, *args, **kwargs): + s = sensors.ExtractSensors() + s.execute() + self.assertTrue(s.success) + s_ob = Sensors.objects.all() + self.assertTrue(s_ob) diff --git a/Submodules/GreedyBear/tests/test_models.py b/Submodules/GreedyBear/tests/test_models.py new file mode 100644 index 0000000..e6a23af --- /dev/null +++ b/Submodules/GreedyBear/tests/test_models.py @@ -0,0 +1,32 @@ +from greedybear.models import Statistics, iocType, viewType + +from . import CustomTestCase + + +class ModelsTestCase(CustomTestCase): + def test_ioc_model(self): + self.assertEqual(self.ioc.name, "140.246.171.141") + self.assertEqual(self.ioc.type, iocType.IP.value) + self.assertEqual(self.ioc.first_seen, self.current_time) + self.assertEqual(self.ioc.last_seen, self.current_time) + self.assertEqual(self.ioc.days_seen, [self.current_time]) + self.assertEqual(self.ioc.number_of_days_seen, 1) + self.assertEqual(self.ioc.times_seen, 1) + self.assertEqual(self.ioc.log4j, True) + self.assertEqual(self.ioc.cowrie, True) + self.assertEqual(self.ioc.scanner, True) + self.assertEqual(self.ioc.payload_request, True) + self.assertEqual(self.ioc.related_urls, []) + + self.assertIn(self.heralding, self.ioc.general_honeypot.all()) + self.assertIn(self.ciscoasa, self.ioc.general_honeypot.all()) + + def test_statistics_model(self): + self.statistic = Statistics.objects.create(source="140.246.171.141", view=viewType.ENRICHMENT_VIEW.value, request_date=self.current_time) + self.assertEqual(self.statistic.source, "140.246.171.141") + self.assertEqual(self.statistic.view, viewType.ENRICHMENT_VIEW.value) + self.assertEqual(self.statistic.request_date, self.current_time) + + def test_general_honeypot_model(self): + self.assertEqual(self.heralding.name, "Heralding") + self.assertEqual(self.heralding.active, True) diff --git a/Submodules/GreedyBear/tests/test_serializers.py b/Submodules/GreedyBear/tests/test_serializers.py new file mode 100644 index 0000000..b8e2dbc --- /dev/null +++ b/Submodules/GreedyBear/tests/test_serializers.py @@ -0,0 +1,105 @@ +from datetime import datetime +from itertools import product + +from api.serializers import FeedsResponseSerializer, FeedsSerializer +from django.test import TestCase +from greedybear.consts import PAYLOAD_REQUEST, SCANNER +from greedybear.models import GeneralHoneypot +from rest_framework.serializers import ValidationError + + +class FeedsSerializersTestCase(TestCase): + @classmethod + def setUpClass(self): + GeneralHoneypot.objects.create( + name="adbhoney", + active=True, + ) + + @classmethod + def tearDownClass(self): + # db clean + GeneralHoneypot.objects.all().delete() + + def test_valid_fields(self): + feed_type_choices = ["all", "log4j", "cowrie", "adbhoney"] + attack_type_choices = ["all", "scanner", "payload_request"] + age_choices = ["recent", "persistent"] + format_choices = ["txt", "json", "csv"] + # genereted all the possible valid input data using cartesian product + valid_data_choices = product(feed_type_choices, attack_type_choices, age_choices, format_choices) + + for element in valid_data_choices: + data_ = {"feed_type": element[0], "attack_type": element[1], "age": element[2], "format": element[3]} + serializer = FeedsSerializer(data=data_) + valid = serializer.is_valid(raise_exception=True) + self.assertEqual(valid, True) + + def test_invalid_fields(self): + data_ = {"feed_type": "invalid_feed_type", "attack_type": "invalid_attack_type", "age": "invalid_age", "format": "invalid_format"} + serializer = FeedsSerializer(data=data_) + try: + serializer.is_valid(raise_exception=True) + except ValidationError: + self.assertIn("feed_type", serializer.errors) + self.assertIn("attack_type", serializer.errors) + self.assertIn("age", serializer.errors) + self.assertIn("format", serializer.errors) + + +class FeedsResponseSerializersTestCase(TestCase): + @classmethod + def setUpClass(self): + GeneralHoneypot.objects.create( + name="adbhoney", + active=True, + ) + + @classmethod + def tearDownClass(self): + # db clean + GeneralHoneypot.objects.all().delete() + + def test_valid_fields(self): + scanner_choices = [True, False] + payload_request_choices = [True, False] + feed_type_choices = ["all", "log4j", "cowrie", "adbhoney"] + + # generete all possible valid input data using cartesian product + valid_data_choices = product(scanner_choices, payload_request_choices, feed_type_choices) + + for element in valid_data_choices: + data_ = { + "value": "140.246.171.141", + SCANNER: element[0], + PAYLOAD_REQUEST: element[1], + "first_seen": "2023-03-20", + "last_seen": "2023-03-21", + "times_seen": "5", + "feed_type": element[2], + } + serializer = FeedsResponseSerializer(data=data_) + valid = serializer.is_valid(raise_exception=True) + self.assertEqual(valid, True) + + def test_invalid_fields(self): + data_ = { + "value": True, + SCANNER: "invalid_scanner", + PAYLOAD_REQUEST: "invalid_payload_request", + "first_seen": "31-2023-03", + "last_seen": "31-2023-03", + "times_seen": "invalid_times_seen", + "feed_type": "invalid_feed_type", + } + serializer = FeedsResponseSerializer(data=data_) + try: + serializer.is_valid(raise_exception=True) + except ValidationError: + self.assertIn("value", serializer.errors) + self.assertIn(SCANNER, serializer.errors) + self.assertIn(PAYLOAD_REQUEST, serializer.errors) + self.assertIn("first_seen", serializer.errors) + self.assertIn("last_seen", serializer.errors) + self.assertIn("times_seen", serializer.errors) + self.assertIn("feed_type", serializer.errors) diff --git a/Submodules/GreedyBear/tests/test_views.py b/Submodules/GreedyBear/tests/test_views.py new file mode 100644 index 0000000..9a76f85 --- /dev/null +++ b/Submodules/GreedyBear/tests/test_views.py @@ -0,0 +1,152 @@ +from greedybear.consts import FEEDS_LICENSE +from greedybear.models import GeneralHoneypot, Statistics, viewType +from rest_framework.test import APIClient + +from . import CustomTestCase + + +class EnrichmentViewTestCase(CustomTestCase): + def setUp(self): + # setup client + self.client = APIClient() + self.client.force_authenticate(user=self.superuser) + + def test_for_vaild_unregistered_ip(self): + """Check for a valid IP that is unavaliable in DB""" + response = self.client.get("/api/enrichment?query=192.168.0.1") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["found"], False) + + def test_for_invaild_unregistered_ip(self): + """Check for a IP that Fails Regex Checks and is unavaliable in DB""" + response = self.client.get("/api/enrichment?query=30.168.1.255.1") + self.assertEqual(response.status_code, 400) + + def test_for_vaild_registered_ip(self): + """Check for a valid IP that is avaliable in DB""" + response = self.client.get("/api/enrichment?query=140.246.171.141") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["found"], True) + self.assertEqual(response.json()["ioc"]["name"], self.ioc.name) + self.assertEqual(response.json()["ioc"]["type"], self.ioc.type) + self.assertEqual( + response.json()["ioc"]["first_seen"], + self.ioc.first_seen.isoformat(sep="T", timespec="microseconds"), + ) + self.assertEqual( + response.json()["ioc"]["last_seen"], + self.ioc.last_seen.isoformat(sep="T", timespec="microseconds"), + ) + self.assertEqual(response.json()["ioc"]["number_of_days_seen"], self.ioc.number_of_days_seen) + self.assertEqual(response.json()["ioc"]["times_seen"], self.ioc.times_seen) + self.assertEqual(response.json()["ioc"]["log4j"], self.ioc.log4j) + self.assertEqual(response.json()["ioc"]["cowrie"], self.ioc.cowrie) + self.assertEqual(response.json()["ioc"]["general_honeypot"][0], self.heralding.name) # FEEDS + self.assertEqual(response.json()["ioc"]["general_honeypot"][1], self.ciscoasa.name) # FEEDS + self.assertEqual(response.json()["ioc"]["scanner"], self.ioc.scanner) + self.assertEqual(response.json()["ioc"]["payload_request"], self.ioc.payload_request) + + def test_for_invalid_authentication(self): + """Check for a invalid authentication""" + self.client.logout() + response = self.client.get("/api/enrichment?query=140.246.171.141") + self.assertEqual(response.status_code, 401) + + +class FeedsViewTestCase(CustomTestCase): + def test_200_all_feeds(self): + response = self.client.get("/api/feeds/all/all/recent.json") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["license"], FEEDS_LICENSE) + self.assertEqual(response.json()["iocs"][0]["feed_type"], "log4j") + self.assertEqual(response.json()["iocs"][0]["times_seen"], 1) + self.assertEqual(response.json()["iocs"][0]["scanner"], True) + self.assertEqual(response.json()["iocs"][0]["payload_request"], True) + + def test_200_general_feeds(self): + response = self.client.get("/api/feeds/heralding/all/recent.json") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["license"], FEEDS_LICENSE) + self.assertEqual(response.json()["iocs"][0]["feed_type"], "heralding") + self.assertEqual(response.json()["iocs"][0]["times_seen"], 1) + self.assertEqual(response.json()["iocs"][0]["scanner"], True) + self.assertEqual(response.json()["iocs"][0]["payload_request"], True) + + def test_400_feeds(self): + response = self.client.get("/api/feeds/test/all/recent.json") + self.assertEqual(response.status_code, 400) + + def test_200_feeds_pagination(self): + response = self.client.get("/api/feeds/?page_size=10&page=1&feed_type=all&attack_type=all&age=recent") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["count"], 1) + self.assertEqual(response.json()["total_pages"], 1) + + def test_400_feeds_pagination(self): + response = self.client.get("/api/feeds/?page_size=10&page=1&feed_type=all&attack_type=test&age=recent") + self.assertEqual(response.status_code, 400) + + +class StatisticsViewTestCase(CustomTestCase): + @classmethod + def setUpClass(self): + super(StatisticsViewTestCase, self).setUpClass() + Statistics.objects.all().delete() + Statistics.objects.create(source="140.246.171.141", view=viewType.FEEDS_VIEW.value) + Statistics.objects.create(source="140.246.171.141", view=viewType.ENRICHMENT_VIEW.value) + + @classmethod + def tearDownClass(self): + Statistics.objects.all().delete() + + def test_200_feeds_sources(self): + response = self.client.get("/api/statistics/sources/feeds") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0]["Sources"], 1) + + def test_200_feeds_downloads(self): + response = self.client.get("/api/statistics/downloads/feeds") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0]["Downloads"], 1) + + def test_200_enrichment_sources(self): + response = self.client.get("/api/statistics/sources/enrichment") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0]["Sources"], 1) + + def test_200_enrichment_requests(self): + response = self.client.get("/api/statistics/requests/enrichment") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0]["Requests"], 1) + + def test_200_feed_types(self): + self.assertEqual(GeneralHoneypot.objects.count(), 2) + # add a general honeypot without associated ioc + GeneralHoneypot(name="Tanner", active=True).save() + self.assertEqual(GeneralHoneypot.objects.count(), 3) + + response = self.client.get("/api/statistics/feeds_types") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0]["Heralding"], 1) + self.assertEqual(response.json()[0]["Ciscoasa"], 1) + self.assertEqual(response.json()[0]["Log4j"], 1) + self.assertEqual(response.json()[0]["Cowrie"], 1) + self.assertEqual(response.json()[0]["Tanner"], 0) + + +class GeneralHoneypotViewTestCase(CustomTestCase): + def test_200_all_general_honeypots(self): + self.assertEqual(GeneralHoneypot.objects.count(), 2) + # add a general honeypot not active + GeneralHoneypot(name="Adbhoney", active=False).save() + self.assertEqual(GeneralHoneypot.objects.count(), 3) + + response = self.client.get("/api/general_honeypot") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), ["Heralding", "Ciscoasa", "Adbhoney"]) + + def test_200_active_general_honeypots(self): + self.assertEqual(GeneralHoneypot.objects.count(), 2) + response = self.client.get("/api/general_honeypot?onlyActive=true") + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), ["Heralding", "Ciscoasa"]) diff --git a/Submodules/IntelOwl/LICENSE b/Submodules/IntelOwl/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/Submodules/IntelOwl/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/Submodules/IntelOwl/api_app/__init__.py b/Submodules/IntelOwl/api_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/admin.py b/Submodules/IntelOwl/api_app/admin.py new file mode 100644 index 0000000..8ad3dab --- /dev/null +++ b/Submodules/IntelOwl/api_app/admin.py @@ -0,0 +1,223 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Any + +from django.contrib import admin +from django.contrib.admin import widgets +from django.db.models import JSONField, ManyToManyField +from django.http import HttpRequest +from prettyjson.widgets import PrettyJSONWidget + +from .forms import OrganizationPluginConfigurationForm, ParameterInlineForm +from .models import ( + AbstractConfig, + Job, + OrganizationPluginConfiguration, + Parameter, + PluginConfig, + PythonModule, + Tag, +) +from .tabulars import ( + OrganizationPluginConfigurationInLine, + ParameterInline, + PluginConfigInlineForParameter, + PluginConfigInlineForPythonConfig, +) + + +class CustomAdminView(admin.ModelAdmin): + formfield_overrides = { + JSONField: {"widget": PrettyJSONWidget(attrs={"initial": "parsed"})}, + } + + def formfield_for_manytomany( + self, db_field: ManyToManyField, request: HttpRequest, **kwargs: Any + ): + vertical = False + kwargs["widget"] = widgets.FilteredSelectMultiple( + verbose_name=db_field.verbose_name, is_stacked=vertical + ) + return super().formfield_for_manytomany(db_field, request, **kwargs) + + +@admin.register(Job) +class JobAdminView(CustomAdminView): + list_display = ( + "id", + "status", + "user", + "observable_name", + "observable_classification", + "file_name", + "file_mimetype", + "received_request_time", + "analyzers_executed", + "connectors_executed", + "visualizers_executed", + "get_tags", + ) + list_display_link = ( + "id", + "user", + "status", + ) + search_fields = ( + "md5", + "observable_name", + "file_name", + ) + list_filter = ("status", "user", "tags") + + def has_add_permission(self, request: HttpRequest) -> bool: + return False + + def has_change_permission(self, request: HttpRequest, obj=None) -> bool: + return False + + @admin.display(description="Tags") + def get_tags(self, instance: Job): + return [tag.label for tag in instance.tags.all()] + + @staticmethod + def analyzers_executed(instance: Job): # noqa + return [analyzer.name for analyzer in instance.analyzers_to_execute.all()] + + @staticmethod + def connectors_executed(instance: Job): # noqa + return [connector.name for connector in instance.connectors_to_execute.all()] + + @staticmethod + def visualizers_executed(instance: Job): # noqa + return [visualizer.name for visualizer in instance.visualizers_to_execute.all()] + + +@admin.register(Tag) +class TagAdminView(CustomAdminView): + list_display = ("id", "label", "color") + search_fields = ("label", "color") + + +class ModelWithOwnershipAdminView: + list_display = ( + "for_organization", + "get_owner", + ) + list_filter = ("for_organization", "owner") + + @admin.display(description="Owner") + def get_owner(self, instance: PluginConfig): + if instance.owner: + return instance.owner.username + return "-" + + +@admin.register(PluginConfig) +class PluginConfigAdminView(ModelWithOwnershipAdminView, CustomAdminView): + list_display = ( + "pk", + "get_config", + "parameter", + "for_organization", + "get_owner", + "get_type", + "value", + ) + ModelWithOwnershipAdminView.list_display + + search_fields = ["parameter__name", "value"] + + @admin.display(description="Config") + def get_config(self, instance: PluginConfig): + return instance.config.name + + @admin.display(description="Type") + def get_type(self, instance: PluginConfig): + return instance.parameter.type + + +class AbstractReportAdminView(CustomAdminView): + list_display = ( + "id", + "config", + "job", + "status", + "start_time", + "end_time", + ) + list_display_links = ("id",) + search_fields = ("config",) + + @staticmethod + def has_add_permission(request): + return False + + def has_change_permission(self, request: HttpRequest, obj=None) -> bool: + return False + + +@admin.register(Parameter) +class ParameterAdminView(CustomAdminView): + inlines = [PluginConfigInlineForParameter] + search_fields = ["name"] + list_filter = ["is_secret"] + list_display = ParameterInlineForm.Meta.fields + fields = list_display + + +@admin.register(PythonModule) +class PythonModuleAdminView(CustomAdminView): + list_display = [ + "module", + "base_path", + "get_parameters", + "get_secrets", + "update_schedule", + "health_check_schedule", + ] + search_fields = ["module", "base_path"] + list_filter = ["base_path"] + inlines = [ParameterInline] + + @admin.display(description="Parameters") + def get_parameters(self, obj: PythonModule): + return list(obj.parameters.filter(is_secret=False).order_by("-name")) + + @admin.display(description="Secrets") + def get_secrets(self, obj: PythonModule): + return list(obj.parameters.filter(is_secret=True).order_by("-name")) + + +class AbstractConfigAdminView(CustomAdminView): + list_display = ("name", "description", "disabled", "disabled_in_orgs") + search_fields = ("name",) + list_filter = ("disabled",) + # allow to clone the object + save_as = True + + @admin.display(description="Disabled in orgs") + def disabled_in_orgs(self, instance: AbstractConfig): + return list( + instance.orgs_configuration.filter(disabled=True).values_list( + "organization__name", flat=True + ) + ) + + +class PythonConfigAdminView(AbstractConfigAdminView): + list_display = AbstractConfigAdminView.list_display + ("routing_key",) + inlines = [PluginConfigInlineForPythonConfig, OrganizationPluginConfigurationInLine] + list_filter = ["routing_key"] + + +@admin.register(OrganizationPluginConfiguration) +class OrganizationPluginConfigurationAdminView(CustomAdminView): + list_display = [ + "config", + "organization", + "disabled", + "disabled_comment", + "rate_limit_timeout", + ] + exclude = ["content_type", "object_id"] + list_filter = ["organization", "content_type"] + form = OrganizationPluginConfigurationForm diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/admin.py b/Submodules/IntelOwl/api_app/analyzers_manager/admin.py new file mode 100644 index 0000000..2473f3e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/admin.py @@ -0,0 +1,22 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from django.contrib import admin + +from api_app.admin import AbstractReportAdminView, PythonConfigAdminView +from api_app.analyzers_manager.models import AnalyzerConfig, AnalyzerReport + + +@admin.register(AnalyzerReport) +class AnalyzerReportAdminView(AbstractReportAdminView): + ... + + +@admin.register(AnalyzerConfig) +class AnalyzerConfigAdminView(PythonConfigAdminView): + list_display = PythonConfigAdminView.list_display + ( + "type", + "docker_based", + "maximum_tlp", + ) + list_filter = ["type", "maximum_tlp"] + PythonConfigAdminView.list_filter + exclude = ["update_task"] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/apps.py b/Submodules/IntelOwl/api_app/analyzers_manager/apps.py new file mode 100644 index 0000000..e54efa8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class AnalyzersManagerConfig(AppConfig): + name = "api_app.analyzers_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/classes.py b/Submodules/IntelOwl/api_app/analyzers_manager/classes.py new file mode 100644 index 0000000..3ee239f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/classes.py @@ -0,0 +1,475 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +import logging +import time +from abc import ABCMeta +from pathlib import PosixPath +from typing import Dict, Tuple + +import requests +from django.conf import settings + +from certego_saas.apps.user.models import User +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..choices import PythonModuleBasePaths +from ..classes import Plugin +from ..models import PythonConfig +from .constants import HashChoices, ObservableTypes, TypeChoices +from .exceptions import AnalyzerConfigurationException, AnalyzerRunException +from .models import AnalyzerConfig, AnalyzerReport + +logger = logging.getLogger(__name__) + + +class BaseAnalyzerMixin(Plugin, metaclass=ABCMeta): + """ + Abstract Base class for Analyzers. + Never inherit from this branch, + always use either one of ObservableAnalyzer or FileAnalyzer classes. + """ + + HashChoices = HashChoices + ObservableTypes = ObservableTypes + TypeChoices = TypeChoices + + @classmethod + @property + def config_exception(cls): + return AnalyzerConfigurationException + + @property + def analyzer_name(self) -> str: + return self._config.name + + @classmethod + @property + def report_model(cls): + return AnalyzerReport + + @classmethod + @property + def config_model(cls): + return AnalyzerConfig + + def get_exceptions_to_catch(self): + """ + Returns additional exceptions to catch when running *start* fn + """ + return ( + AnalyzerConfigurationException, + AnalyzerRunException, + ) + + def _validate_result(self, result, level=0, max_recursion=190): + """ + function to validate result, allowing to store inside postgres without errors. + + If the character \u0000 is present in the string, postgres will throw an error + + If an integer is bigger than max_int, + Mongodb is not capable to store and will throw an error. + + If we have more than 200 recursion levels, every encoding + will throw a maximum_nested_object exception + """ + if level == max_recursion: + logger.info( + f"We have reached max_recursion {max_recursion} level. " + f"The following object will be pruned {result} " + ) + return None + if isinstance(result, dict): + for key, values in result.items(): + result[key] = self._validate_result( + values, level=level + 1, max_recursion=max_recursion + ) + elif isinstance(result, list): + for i, _ in enumerate(result): + result[i] = self._validate_result( + result[i], level=level + 1, max_recursion=max_recursion + ) + elif isinstance(result, str): + return result.replace("\u0000", "") + elif isinstance(result, int) and result > 9223372036854775807: # max int 8bytes + result = 9223372036854775807 + return result + + def after_run_success(self, content): + super().after_run_success(self._validate_result(content, max_recursion=15)) + + +class ObservableAnalyzer(BaseAnalyzerMixin, metaclass=ABCMeta): + """ + Abstract class for Observable Analyzers. + Inherit from this branch when defining a IP, URL or domain analyzer. + Need to overrwrite `set_params(self, params)` + and `run(self)` functions. + """ + + observable_name: str + observable_classification: str + + def __init__( + self, + config: PythonConfig, + **kwargs, + ): + super().__init__(config, **kwargs) + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self._config: AnalyzerConfig + if self._job.is_sample and self._config.run_hash: + self.observable_classification = ObservableTypes.HASH + # check which kind of hash the analyzer needs + run_hash_type = self._config.run_hash_type + if run_hash_type == HashChoices.SHA256: + self.observable_name = self._job.sha256 + else: + self.observable_name = self._job.md5 + else: + self.observable_name = self._job.observable_name + self.observable_classification = self._job.observable_classification + + @classmethod + @property + def python_base_path(cls): + return PythonModuleBasePaths.ObservableAnalyzer.value + + def before_run(self): + super().before_run() + logger.info( + f"STARTED analyzer: {self.__repr__()} -> " + f"Observable: {self.observable_name}." + ) + + def after_run(self): + super().after_run() + logger.info( + f"FINISHED analyzer: {self.__repr__()} -> " + f"Observable: {self.observable_name}." + ) + + +class FileAnalyzer(BaseAnalyzerMixin, metaclass=ABCMeta): + """ + Abstract class for File Analyzers. + Inherit from this branch when defining a file analyzer. + Need to overrwrite `set_params(self, params)` + and `run(self)` functions. + """ + + md5: str + filename: str + file_mimetype: str + + def __init__( + self, + config: PythonConfig, + **kwargs, + ): + super().__init__(config, **kwargs) + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.md5 = self._job.md5 + self.filename = self._job.file_name + # this is updated in the filepath property, like a cache decorator. + # if the filepath is requested, it means that the analyzer downloads... + # ...the file from AWS because it requires a path and it needs to be deleted + self.__filepath = None + self.file_mimetype = self._job.file_mimetype + + @classmethod + @property + def python_base_path(cls) -> PosixPath: + return PythonModuleBasePaths[FileAnalyzer.__name__].value + + def read_file_bytes(self) -> bytes: + self._job.file.seek(0) + return self._job.file.read() + + @property + def filepath(self) -> str: + if not self.__filepath: + self.__filepath = self._job.file.storage.retrieve( + file=self._job.file, analyzer=self.analyzer_name + ) + return self.__filepath + + def before_run(self): + super().before_run() + logger.info( + f"STARTED analyzer: {self.__repr__()} -> " + f"File: ({self.filename}, md5: {self.md5})" + ) + + def after_run(self): + super().after_run() + # We delete the file only if we have single copy for analyzer + # and the file has been saved locally. + # Otherwise we would remove the single file that we have on the server + if not settings.LOCAL_STORAGE and self.filepath is not None: + import os + + try: + os.remove(self.filepath) + except OSError: + logger.warning(f"Filepath {self.filepath} does not exists") + + logger.info( + f"FINISHED analyzer: {self.__repr__()} -> " + f"File: ({self.filename}, md5: {self.md5})" + ) + + +class DockerBasedAnalyzer(BaseAnalyzerMixin, metaclass=ABCMeta): + """ + Abstract class for a docker based analyzer (integration). + Inherit this branch along with either + one of ``ObservableAnalyzer`` or ``FileAnalyzer`` + when defining a docker based analyzer. + See `peframe.py` for example. + + :param name: str + The name of the analyzer service as defined in compose file + and log directory + :param max_tries: int + maximum no. of tries when HTTP polling for result. + :param poll_distance: int + interval between HTTP polling. + """ + + name: str + url: str + max_tries: int + poll_distance: int + key_not_found_max_retries: int = 10 + + @staticmethod + def __raise_in_case_bad_request(name, resp, params_to_check=None) -> bool: + """ + Raises: + :class: `AnalyzerRunException`, if bad status code or no key in response + """ + if params_to_check is None: + params_to_check = ["key"] + # different error messages for different cases + if resp.status_code == 404: + raise AnalyzerConfigurationException( + f"{name} docker container is not running." + ) + if resp.status_code == 400: + err = resp.json().get("error", "") + raise AnalyzerRunException(err) + if resp.status_code == 500: + raise AnalyzerRunException( + f"Internal Server Error in {name} docker container" + ) + # check to make sure there was a valid params in response + for param in params_to_check: + param_value = resp.json().get(param, None) + if not param_value: + raise AnalyzerRunException( + "Unexpected Error. " + f"Please check log files under /var/log/intel_owl/{name.lower()}/" + ) + # just in case couldn't catch the error manually + resp.raise_for_status() + + return True + + @staticmethod + def __query_for_result(url: str, key: str) -> Tuple[int, dict]: + headers = {"Accept": "application/json"} + resp = requests.get(f"{url}?key={key}", headers=headers) + return resp.status_code, resp.json() + + def __polling(self, req_key: str, chance: int, re_poll_try: int = 0): + try: + status_code, json_data = self.__query_for_result(self.url, req_key) + except (requests.RequestException, json.JSONDecodeError) as e: + raise AnalyzerRunException(e) + if status_code == 404: + # This happens when they key does not exist. + # This is possible in case IntelOwl is deployed as a Swarm. + # The previous POST request that created the analysis ... + # ...could have been sent to a different container in another machine. + # so we need to try again and find the server with the key + logger.info( + "Polling again because received a 404." + f" Try #{chance + 1}. Re-Poll try {re_poll_try}. Starting the query..." + f"<-- {self.__repr__()}" + ) + if self.key_not_found_max_retries == re_poll_try: + raise AnalyzerRunException( + f"not found key {req_key} in any server after maximum retries" + ) + return self.__polling(req_key, chance, re_poll_try=re_poll_try + 1) + else: + status = json_data.get("status", None) + if status and status == self._job.Status.RUNNING.value: + logger.info( + f"Poll number #{chance + 1}, " + f"status: 'running' <-- {self.__repr__()}" + ) + else: + return True, json_data + return False, json_data + + def __poll_for_result(self, req_key: str) -> dict: + got_result = False + json_data = {} + for chance in range(self.max_tries): + time.sleep(self.poll_distance) + logger.info( + f"Result Polling. Try #{chance + 1}. Starting the query..." + f"<-- {self.__repr__()}" + ) + got_result, json_data = self.__polling(req_key, chance) + if got_result: + break + + if not got_result: + raise AnalyzerRunException("max polls tried without getting any result.") + return json_data + + def _raise_container_not_running(self) -> None: + raise AnalyzerConfigurationException( + f"{self.name} docker container is not running.\n" + "You have to enable it using the appropriate " + "parameter when executing ./start." + ) + + def _docker_run( + self, req_data: dict, req_files: dict = None, analyzer_name: str = None + ) -> dict: + """ + Helper function that takes of care of requesting new analysis, + reading response, polling for result and exception handling for a + docker based analyzer. + + Args: + req_data (Dict): Dict of request JSON. + req_files (Dict, optional): Dict of files to send. Defaults to None. + analyzer_name: optional, could be used for edge cases + + Raises: + AnalyzerConfigurationException: In case docker service is not running + AnalyzerRunException: Any other error + + Returns: + Dict: Final analysis results + """ + + # step #1: request new analysis + req_data = {**req_data, "force_unique_key": True} + args = req_data.get("args", []) + logger.debug(f"Making request with arguments: {args} <- {self.__repr__()}") + try: + if req_files: + form_data = {"request_json": json.dumps(req_data)} + resp1 = requests.post(self.url, files=req_files, data=form_data) + else: + resp1 = requests.post(self.url, json=req_data) + except requests.exceptions.ConnectionError: + self._raise_container_not_running() + + # step #2: raise AnalyzerRunException in case of error + if not self.__raise_in_case_bad_request(self.name, resp1): + raise AssertionError + + # step #3: if no error, continue and try to fetch result + key = resp1.json().get("key") + final_resp = self.__poll_for_result(key) + err = final_resp.get("error", None) + report = final_resp.get("report", None) + + # APKiD provides empty result in case it does not support the binary type + if not report and (analyzer_name != "APKiD"): + raise AnalyzerRunException(f"Report is empty. Reason: {err}") + + if isinstance(report, dict): + return report + + try: + report = json.loads(report) + except json.JSONDecodeError: + # because report may also be a str only. Example: clamav. + pass + + return report + + def _docker_get(self): + """ + Raises: + AnalyzerConfigurationException: In case docker service is not running + AnalyzerRunException: Any other error + + Returns: + Response: Response object of request + """ + + # step #1: request new analysis + try: + resp = requests.get(url=self.url) + except requests.exceptions.ConnectionError: + self._raise_container_not_running() + + # step #2: raise AnalyzerRunException in case of error + if not self.__raise_in_case_bad_request(self.name, resp, params_to_check=[]): + raise AssertionError + return resp + + @staticmethod + def mocked_docker_analyzer_get(*args, **kwargs): + return MockUpResponse( + {"key": "test", "returncode": 0, "report": {"test": "This is a test."}}, 200 + ) + + @staticmethod + def mocked_docker_analyzer_post(*args, **kwargs): + return MockUpResponse({"key": "test", "status": "running"}, 202) + + def _monkeypatch(self, patches: list = None): + """ + Here, `_monkeypatch` is an instance method and not a class method. + This is because when defined with `@classmethod`, we were getting the error + ``` + '_patch' object has no attribute 'is_local' + ``` + whenever multiple analyzers with same parent class were being called. + """ + if patches is None: + patches = [] + # no need to sleep during tests + self.poll_distance = 0 + patches.append( + if_mock_connections( + patch( + "requests.get", + side_effect=self.mocked_docker_analyzer_get, + ), + patch( + "requests.post", + side_effect=self.mocked_docker_analyzer_post, + ), + ) + ) + return super()._monkeypatch(patches) + + def health_check(self, user: User = None) -> bool: + """ + basic health check: if instance is up or not (timeout - 10s) + """ + try: + requests.head(self.url, timeout=10) + except requests.exceptions.RequestException: + health_status = False + else: + health_status = True + + return health_status diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/constants.py b/Submodules/IntelOwl/api_app/analyzers_manager/constants.py new file mode 100644 index 0000000..45d55c5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/constants.py @@ -0,0 +1,85 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import ipaddress +import re +from logging import getLogger + +from django.db import models + +logger = getLogger(__name__) + + +class TypeChoices(models.TextChoices): + FILE = "file" + OBSERVABLE = "observable" + + +class HashChoices(models.TextChoices): + MD5 = "md5" + SHA256 = "sha256" + + +class ObservableTypes(models.TextChoices): + IP = "ip" + URL = "url" + DOMAIN = "domain" + HASH = "hash" + GENERIC = "generic" + + @classmethod + def calculate(cls, value: str) -> str: + """Returns observable classification for the given value.\n + Only following types are supported: + ip, domain, url, hash (md5, sha1, sha256), generic (if no match) + + Args: + value (str): + observable value + Returns: + str: one of `ip`, `url`, `domain`, `hash` or 'generic'. + """ + try: + ipaddress.ip_address(value) + except ValueError: + if re.match( + r"^.+://[a-z\d-]{1,200}" + r"(?:\.[a-zA-Z\d\u2044\u2215!#$&(-;=?-\[\]_~]{1,200})+" + r"(?::\d{2,6})?" + r"(?:/[a-zA-Z\d\u2044\u2215!#$&(-;=?-\[\]_~]{1,200})*" + r"(?:\.\w+)?", + value, + ): + classification = cls.URL + elif re.match( + r"^([\[\\]?\.[\]\\]?)?[a-z\d-]{1,63}" + r"(([\[\\]?\.[\]\\]?)[a-z\d-]{1,63})+$", + value, + re.IGNORECASE, + ): + classification = cls.DOMAIN + elif ( + re.match(r"^[a-f\d]{32}$", value, re.IGNORECASE) + or re.match(r"^[a-f\d]{40}$", value, re.IGNORECASE) + or re.match(r"^[a-f\d]{64}$", value, re.IGNORECASE) + ): + classification = cls.HASH + else: + classification = cls.GENERIC + logger.info( + "Couldn't detect observable classification" + f" for {value}, setting as 'generic'" + ) + else: + # it's a simple IP + classification = cls.IP + + return classification + + +class AllTypes(models.TextChoices): + IP = "ip" + URL = "url" + DOMAIN = "domain" + HASH = "hash" + GENERIC = "generic" + FILE = "file" diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/exceptions.py b/Submodules/IntelOwl/api_app/analyzers_manager/exceptions.py new file mode 100644 index 0000000..d616564 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/exceptions.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +class NotRunnableAnalyzer(Exception): + pass + + +class AnalyzerRunException(Exception): + pass + + +class AnalyzerConfigurationException(Exception): + pass diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/apkid.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/apkid.py new file mode 100644 index 0000000..890077d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/apkid.py @@ -0,0 +1,42 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# CARE! There is a python apkid package but that conflicts +# with currently installed yara-python version +# Because of this, it is better to handle this separately with his own environment + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class APKiD(FileAnalyzer, DockerBasedAnalyzer): + name: str = "apk_analyzers" + url: str = "http://malware_tools_analyzers:4002/apkid" + # http request polling max number of tries + max_tries: int = 10 + # interval between http request polling (in secs) + poll_distance: int = 3 + + def run(self): + # construct a valid filename into which thug will save the result + fname = str(self.filename).replace("/", "_").replace(" ", "_") + # get the file to send + binary = self.read_file_bytes() + # construct arguments, For example this corresponds to, + # apkid -j file.apk + args = [ + "-t", + "20", + "-j", + f"@{fname}", + ] + req_data = { + "args": args, + } + req_files = {fname: binary} + + report = self._docker_run(req_data, req_files, analyzer_name=self.analyzer_name) + if not report: + # APKiD provides empty result in case it does not support the binary type + self.report.errors.append("APKiD does not support the file") + return {} + return report diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/blint_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/blint_scan.py new file mode 100644 index 0000000..1e8de81 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/blint_scan.py @@ -0,0 +1,37 @@ +import logging +import os +import shutil + +from blint.analysis import AnalysisRunner +from django.conf import settings + +from api_app.analyzers_manager.classes import FileAnalyzer +from intel_owl.settings._util import set_permissions + +logger = logging.getLogger(__name__) + + +class BlintAnalyzer(FileAnalyzer): + """ + Wrapper for Blint static analysis tool + """ + + def update(self) -> bool: + pass + + def run(self) -> dict: + logger.info(f"Running Blint on {self.filepath} for {self.md5}") + + reports_dir = settings.BLINT_REPORTS_PATH / f"blint_analysis_{self.md5}" + os.mkdir(reports_dir) + set_permissions(reports_dir) + + analyzer = AnalysisRunner() + findings, reviews, fuzzables = analyzer.start( + files=[self.filepath], reports_dir=reports_dir + ) + response = {"findings": findings, "reviews": reviews, "fuzzables": fuzzables} + + shutil.rmtree(reports_dir) + + return response diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/boxjs_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/boxjs_scan.py new file mode 100644 index 0000000..4fcb968 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/boxjs_scan.py @@ -0,0 +1,40 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class BoxJS(FileAnalyzer, DockerBasedAnalyzer): + name: str = "box-js" + url: str = "http://malware_tools_analyzers:4002/boxjs" + # http request polling max number of tries + max_tries: int = 5 + # interval between http request polling (in secs) + poll_distance: int = 12 + + def run(self): + # construct a valid filename into which thug will save the result + fname = str(self.filename).replace("/", "_").replace(" ", "_") + # get the file to send + binary = self.read_file_bytes() + # construct arguments, For example this corresponds to, + # box-js sample.js --output-dir=/tmp/boxjs --no-kill ... + args = [ + f"@{fname}", + "--output-dir=/tmp/boxjs", + "--no-kill", + "--no-shell-error", + "--no-echo", + ] + # Box-js by default has a timeout of 10 seconds, + # but subprocess is not able to catch that + # that's why it's necessary to provide a custom timeout of + # 10 seconds only to the subprocess itself. + req_data = { + "args": args, + "timeout": 10, + "callback_context": {"read_result_from": fname}, + } + req_files = {fname: binary} + + return self._docker_run(req_data, req_files) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/capa_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/capa_info.py new file mode 100644 index 0000000..bf5a394 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/capa_info.py @@ -0,0 +1,40 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class CapaInfo(FileAnalyzer, DockerBasedAnalyzer): + name: str = "Capa" + url: str = "http://malware_tools_analyzers:4002/capa" + # interval between http request polling + poll_distance: int = 10 + # http request polling max number of tries + max_tries: int = 60 + # here, max_tries * poll_distance = 10 minutes + timeout: int = 60 * 9 + # whereas subprocess timeout is kept as 60 * 9 = 9 minutes + + shellcode: bool + arch: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.args = [] + if self.arch != "64": + self.arch = "32" + if self.shellcode: + self.args.append("-f") + self.args.append("sc" + self.arch) + + def run(self): + # get binary + binary = self.read_file_bytes() + # make request data + fname = str(self.filename).replace("/", "_").replace(" ", "_") + args = [f"@{fname}", *self.args] + req_data = {"args": args, "timeout": self.timeout} + req_files = {fname: binary} + + return self._docker_run(req_data, req_files) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/cape_sandbox.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/cape_sandbox.py new file mode 100644 index 0000000..198cf4e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/cape_sandbox.py @@ -0,0 +1,362 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time +from tempfile import NamedTemporaryFile +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class CAPEsandbox(FileAnalyzer): + class ContinuePolling(Exception): + pass + + # Specify options for the analysis package (e.g. "name=value,name2=value2"). + options: str + # Specify an analysis package. + package: str + # Specify an analysis timeout. + timeout: int + # Specify a priority for the analysis (1=low, 2=medium, 3=high). + priority: int + # Specify the identifier of a machine you want to use (empty = first available). + machine: str + # Specify the operating system platform you want to use (windows/darwin/linux). + platform: str + # Enable to take a memory dump of the analysis machine. + memory: bool + # Enable to force the analysis to run for the full timeout period. + enforce_timeout: bool + # Specify any custom value. + custom: str + # Specify tags identifier of a machine you want to use. + tags: str + # Specify an analysis route. + route: str + # Number of max tries while trying to poll the CAPESandbox API. + max_tries: int + # Seconds to wait before moving on to the next poll attempt. + poll_distance: int + # Python requests HTTP GET/POST timeout + requests_timeout: int + # Token for Token Auth. + _api_key_name: str + # URL for the CapeSandbox instance. + _url_key_name: str + # CapeSandbox SSL certificate (multiline string). + _certificate: str + + @staticmethod + def _clean_certificate(cert): + return ( + cert.replace("-----BEGIN CERTIFICATE-----", "-----BEGIN_CERTIFICATE-----") + .replace("-----END CERTIFICATE-----", "-----END_CERTIFICATE-----") + .replace(" ", "\n") + .replace("-----BEGIN_CERTIFICATE-----", "-----BEGIN CERTIFICATE-----") + .replace("-----END_CERTIFICATE-----", "-----END CERTIFICATE-----") + ) + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.__cert_file = NamedTemporaryFile(mode="w") + self.__cert_file.write(self._clean_certificate(self._certificate)) + self.__cert_file.flush() + self.__session = requests.Session() + self.__session.verify = self.__cert_file.name + self.__session.headers = { + "Authorization": f"Token {self._api_key_name}", + } + + def run(self): + api_url: str = self._url_key_name + "/apiv2/tasks/create/file/" + to_respond = {} + logger.info(f"Job: {self.job_id} -> " "Starting file upload.") + + cape_params_name = [ + "options", + "package", + "timeout", + "priority", + "machine", + "platform", + "memory", + "enforce_timeout", + "custom", + "tags", + "route", + ] + data = { + name: getattr(self, name) + for name in cape_params_name + if getattr(self, name, None) is not None + } + + try: + response = self.__session.post( + api_url, + files={ + "file": (self.filename, self.read_file_bytes()), + }, + data=data, + timeout=self.requests_timeout, + ) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + response_json = response.json() + logger.debug(f"response received: {response_json}") + response_error = response_json.get("error", False) + + if not response_error: + task_id = response_json.get("data").get("task_ids")[0] + result = self.__poll_for_result(task_id=task_id) + to_respond["result_url"] = self._url_key_name + f"/submit/status/{task_id}/" + to_respond["response"] = result + logger.info( + f"Job: {self.job_id} -> " + "File uploaded successfully without any errors." + ) + + else: + response_errors = response_json.get("errors", []) + if response_errors: + values = list(response_errors[0].values()) + if ( + values + and values[0] + == "Not unique, as unique option set on submit or in conf/web.conf" + ): + # The above response is only returned when + # a sample that has been already + # uploaded once is uploaded again. + + # If it has been then we can just check it's + # report by querying the CAPESandbox API with the md5 hash of + # the file. + + # If it exists in their database and is readable by us, + # the following code further fetches it's information. + # response_json in this case should look somewhat like this: + + # { + # 'error': True, + # 'error_value': 'Error adding task to database', + # 'errors': [{ + # 'filename.exe': + # 'Not unique, as unique option set + # on submit or in conf/web.conf' + # }] + # } + + logger.info( + f"Job: {self.job_id} -> " + "File uploaded is already present in the database. " + "Querying its information through it's md5 hash.." + ) + + status_id = self.__search_by_md5() + gui_report_url = self._url_key_name + "/submit/status/" + status_id + report_url = ( + self._url_key_name + + "/apiv2/tasks/get/report/" + + status_id + + "/litereport" + ) + to_respond["result_url"] = gui_report_url + + try: + final_request = self.__session.get( + report_url, timeout=self.requests_timeout + ) + except requests.RequestException as e: + raise AnalyzerRunException(e) + + to_respond["response"] = final_request.json() + + return to_respond + + def __search_by_md5(self) -> str: + db_search_url = self._url_key_name + "/apiv2/tasks/search/md5/" + self.md5 + + try: + q = self.__session.get(db_search_url, timeout=self.requests_timeout) + q.raise_for_status() + + except requests.RequestException as e: + raise AnalyzerRunException(e) + + data_list = q.json().get("data") + if not data_list: + raise AnalyzerRunException( + "'data' key in response isn't populated in __search_by_md5 as expected" + ) + + status_id_int = data_list[0].get("id") + status_id = str(status_id_int) + return status_id + + def __single_poll(self, url, polling=True): + try: + response = self.__session.get(url, timeout=self.requests_timeout) + # 429 Rate Limit is caught by the raise_for_status + response.raise_for_status() + except requests.RequestException as e: + if polling: + raise self.ContinuePolling(f"RequestException {e}") + else: + raise AnalyzerRunException(e) + return response + + def __poll_for_result( + self, + task_id, + ) -> dict: + # decreasing timeout: + # average analysis starting time -> the request is still made in case of mock + # real analysis duration with poll distance of 1 minute + the remaining module + # empirical value of processing duration time + # real polling with max tries + # final attempts in case there is a bottleneck + timeout_attempts = ( + [30] + + [60] * (self.timeout // 60) + + [self.timeout % 60] + + [50] + + [self.poll_distance] * self.max_tries + + [60] * 3 + ) + + tot_time = sum(timeout_attempts) + if tot_time > 600: + logger.warning( + f" Job: {self.job_id} -> " + "Broken soft time limit!! " + f"The analysis in the worst case will last {tot_time} seconds" + ) + + results = None + success = False + status_api = self._url_key_name + "/apiv2/tasks/status/" + str(task_id) + is_pending = True + + while is_pending: # starts the for loop when we are not in pending state + is_pending = False # ends the timeouts loop but only this time + for try_, curr_timeout in enumerate(timeout_attempts): + attempt = try_ + 1 + try: + logger.info( + f" Job: {self.job_id} -> " + f"Starting poll number #{attempt}/{len(timeout_attempts)}" + ) + + request = self.__single_poll(status_api) + + # in case the request was ok + responded_json = request.json() + error = responded_json.get("error") + data = responded_json.get("data") + + logger.info( + f"Job: {self.job_id} -> " + f"Status of the CAPESandbox task: {data}" + ) + + if error: + raise AnalyzerRunException(error) + + if data == "pending": + is_pending = True + logger.info( + f" Job: {self.job_id} -> " + "Waiting for the pending status to end, " + "sleeping for 15 seconds..." + ) + time.sleep(15) + break + + if data in ("running", "processing"): + raise self.ContinuePolling(f"Task still {data}") + + if data in ("reported", "completed"): + report_url = ( + self._url_key_name + + "/apiv2/tasks/get/report/" + + str(task_id) + + "/litereport" + ) + + results = self.__single_poll(report_url, polling=False).json() + + # the task was being processed + if ( + "error" in results + and results["error"] + and results["error_value"] == "Task is still being analyzed" + ): + raise self.ContinuePolling("Task still processing") + + logger.info( + f" Job: {self.job_id} ->" + f"Poll number #{attempt}/{len(timeout_attempts)} fetched" + " the results of the analysis." + " stopping polling.." + ) + success = True + + break + + else: + raise AnalyzerRunException( + f"status {data} was unexpected. Check the code" + ) + + except self.ContinuePolling as e: + logger.info( + f"Job: {self.job_id} -> " + "Continuing the poll at attempt number: " + f"#{attempt}/{len(timeout_attempts)}. {e}. " + f"Sleeping for {curr_timeout} seconds." + ) + if try_ != self.max_tries - 1: # avoiding useless last sleep + time.sleep(curr_timeout) + + if not success: + raise AnalyzerRunException(f"{self.job_id} poll ended without results") + return results + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + return_value=MockUpResponse( + {"error": False, "data": "completed"}, 200 + ), + ), + patch( + "requests.Session.post", + return_value=MockUpResponse( + { + "error": False, + "data": { + "task_ids": [1234], + }, + "errors": [], + "url": ["http://fake_url.com/submit/status/1234/"], + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/clamav.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/clamav.py new file mode 100644 index 0000000..5ae0f9b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/clamav.py @@ -0,0 +1,54 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer +from tests.mock_utils import MockUpResponse + +logger = logging.getLogger(__name__) + + +class ClamAV(FileAnalyzer, DockerBasedAnalyzer): + name: str = "ClamAV" + url: str = "http://malware_tools_analyzers:4002/clamav" + # interval between http request polling + poll_distance: int = 3 + # http request polling max number of tries + max_tries: int = 20 + # timeout limit + timeout: int = 60 + + def run(self): + # get binary + binary = self.read_file_bytes() + # make request data + fname = str(self.filename).replace("/", "_").replace(" ", "_") + args = [f"@{fname}"] + req_data = {"args": args, "timeout": self.timeout} + req_files = {fname: binary} + + # report is a string for ClamAV analyzer only + report = self._docker_run(req_data, req_files) + + detections = [] + if "Infected files: 1" in report: + lines = report.split("\n") + for line in lines: + if "SUMMARY" in line: + break + words = line.split() + if words: + signature = words[1] + logger.info(f"extracted signature {signature} for {self.job_id}") + detections.append(signature) + if not detections: + logger.error(f"no detections extracted? {self.job_id}") + + return {"detections": list(set(detections)), "raw_report": report} + + @staticmethod + def mocked_docker_analyzer_get(*args, **kwargs): + return MockUpResponse( + {"key": "test", "returncode": 0, "report": "OK real_signature\n"}, 200 + ) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/cuckoo_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/cuckoo_scan.py new file mode 100644 index 0000000..71e6137 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/cuckoo_scan.py @@ -0,0 +1,279 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import re +import time +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class CuckooAnalysis(FileAnalyzer): + _api_key_name: str + _url_key_name: str + max_post_tries: int + max_poll_tries: int + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + # cuckoo installation can be with or without the api_token + # it depends on version and configuration + self.session = requests.Session() + if not hasattr(self, "_api_key_name"): + logger.info( + f"{self.__repr__()}, (md5: {self.md5}) -> Continuing w/o API key.." + ) + else: + self.session.headers["Authorization"] = f"Bearer {self._api_key_name}" + + self.task_id = 0 + self.result = {} + + def run(self): + binary = self.read_file_bytes() + if not binary: + raise AnalyzerRunException("is the binary empty?!") + + self.__cuckoo_request_scan(binary) + self.__cuckoo_poll_result() + result = self.__cuckoo_retrieve_and_create_report() + + return result + + def __cuckoo_request_scan(self, binary): + logger.info(f"requesting scan for file: ({self.filename},{self.md5})") + + # send the file for analysis + name_to_send = self.filename if self.filename else self.md5 + files = {"file": (name_to_send, binary)} + post_success = False + response = None + for chance in range(self.max_post_tries): + logger.info( + f"request #{chance} for file analysis of ({self.filename},{self.md5})" + ) + response = self.session.post( + self._url_key_name + "tasks/create/file", files=files + ) + if response.status_code != 200: + logger.info( + f"failed post to start cuckoo analysis, status code {response.status_code}" + ) + time.sleep(5) + continue + post_success = True + break + + if post_success: + json_response = response.json() + self.task_id = ( + json_response["task_ids"][0] + if "task_ids" in json_response.keys() + else json_response.get("task_id", 1) + ) + else: + raise AnalyzerRunException( + "failed max tries to post file to cuckoo for analysis" + ) + + def __cuckoo_poll_result(self): + logger.info( + f"polling result for ({self.filename},{self.md5}), task_id: #{self.task_id}" + ) + + # poll for the result + poll_time = 15 + get_success = False + for chance in range(self.max_poll_tries): + logger.info( + f"polling request #{chance + 1} for file ({self.filename},{self.md5})" + ) + url = self._url_key_name + "tasks/view/" + str(self.task_id) + response = self.session.get(url) + json_response = response.json() + status = json_response.get("task", {}).get("status", None) + if status == "reported": + get_success = True + break + elif status == "failed_processing": + raise AnalyzerRunException( + "sandbox analysis failed." + f"cuckoo id: #{self.task_id}, status: 'failed_processing'" + ) + else: + time.sleep(poll_time) + + if not get_success: + raise AnalyzerRunException( + f"sandbox analysis timed out. cuckoo id: #{self.task_id}" + ) + + def __cuckoo_retrieve_and_create_report(self): + logger.info( + f"generating report for ({self.filename},{self.md5}), " + f"task_id #{self.task_id}" + ) + # download the report + response = self.session.get( + self._url_key_name + "tasks/report/" + str(self.task_id) + "/json" + ) + json_response = response.json() + + # extract most IOCs as possible from signatures data reports + signatures = json_response.get("signatures", []) + list_description_signatures = [] + list_detailed_signatures = [] + list_potentially_malicious_urls_marks = [] + list_dyndns_domains_marks = [] + list_potentially_malicious_urls = [] + # flake8: noqa: E501 + regex_url = re.compile( + r"((?:(?:ht|f)tp(?:s?)\:\/\/)(?:[!#$&-;=?-\[\]_a-z~]|%[0-9a-f]{2})+)(?![\)])", + re.I, + ) + for sig in signatures: + sig_description = sig.get("description", "") + sig_name = sig.get("name", "") + sig_severity = sig.get("severity", "") + sig_marks = sig.get("marks", []) + list_description_signatures.append(sig_description) + detailed_signature_data = { + "description": sig_description, + "name": sig_name, + "severity": sig_severity, + "marks": sig_marks, + } + list_detailed_signatures.append(detailed_signature_data) + # get URL marks from some specific signatures + if ( + "malicious URL found" in sig_description + or "External resource URLs" in sig_description + or "Powershell script" in sig_description + ): + list_potentially_malicious_urls_marks.extend(sig_marks) + # get dydns domains from the specific signature + if "networkdyndns_checkip" in sig_name: + list_dyndns_domains_marks.extend(sig_marks) + # look for IOCs extracted from specific signatures + if "suspicious_process" in sig_name: + for suspicious_process_mark in sig_marks: + suspicious_process_ioc = suspicious_process_mark.get("ioc", "") + match_url = re.search(regex_url, suspicious_process_ioc) + if match_url: + list_potentially_malicious_urls.append(match_url.group(1)) + + # extract dyndns domains from specific signature, could be IOCs + dyndns_domains = [] + for mark in list_dyndns_domains_marks: + dydns_ioc = mark.get("ioc", "") + dyndns_domains.append(dydns_ioc) + + # parse specific signatures + for mark in list_potentially_malicious_urls_marks: + ioc = mark.get("ioc", "") + if ioc and ioc.startswith("http"): + list_potentially_malicious_urls.append(ioc) + urls = mark.get("config", {}).get("url", []) + list_potentially_malicious_urls.extend(urls) + + # remove duplicates + list_potentially_malicious_urls = list(set(list_potentially_malicious_urls)) + + # get suricata alerts if available + suricata_alerts = list(json_response.get("suricata", {}).get("alerts", [])) + + # get network data + network_data = json_response.get("network", {}) + uri = [(network["uri"]) for network in network_data.get("http", [])] + domains = [ + {"ip": network["ip"], "domain": network["domain"]} + for network in network_data.get("domains", []) + ] + + # extract all dns domain requested,... + # .. can be used as IOC even if the conn was not successful + dns_answered_list = [] + dns_data = network_data.get("dns", {}) + for dns_dict in dns_data: + # if there are A records and we received an answer + if ( + dns_dict.get("type", "") == "A" + and dns_dict.get("answers", []) + and dns_dict.get("request", "") + ): + dns_answered_list.append(dns_dict["request"]) + + # other info + info_data = json_response.get("info", {}) + # cuckoo magic score + cuckoo_score = info_data.get("score", None) + machine_data = info_data.get("machine", {}) + new_stats = info_data.get("new_stats", None) + cuckoo_id = info_data.get("id", "") + malfamily = json_response.get("malfamily", None) + static = json_response.get("static", {}) + behavior = json_response.get("behavior", {}) + generic_behavior = behavior.get("generic", {}) + api_stats = behavior.get("apistats", {}) + extracted = json_response.get("extracted", {}) + processtree = behavior.get("processtree", {}) + anomaly = behavior.get("anomaly", {}) + debug = json_response.get("debug", {}) + file_data = json_response.get("target", {}).get("file", {}) + file_type = "".join(list(file_data.get("type", ""))) + yara = [yara_match["name"] for yara_match in file_data.get("yara", [])] + + result = { + "link": f"{self._url_key_name}analysis/{cuckoo_id}/summary", + "signatures": list_description_signatures, + "signatures_detailed": list_detailed_signatures, + "suricata_alerts": suricata_alerts, + "potentially_malicious_urls": list_potentially_malicious_urls, + "dyndns_domains": dyndns_domains, + "answered_dns": dns_answered_list, + "domains": domains, + "uri": uri, + "malscore": cuckoo_score, + "malfamily": malfamily, + "new_stats": new_stats, + "file_type": file_type, + "machine": machine_data, + "id": cuckoo_id, + "debug": debug, + "yara": yara, + "static": static, + "behavior": { + "generic_behavior": generic_behavior, + "api_stats": api_stats, + "extracted": extracted, + "processtree": processtree, + "anomaly": anomaly, + }, + } + + logger.info(f"report generated for ({self.filename},{self.md5})") + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + return_value=MockUpResponse({"task": {"status": "reported"}}, 200), + ), + patch( + "requests.Session.post", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/doc_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/doc_info.py new file mode 100644 index 0000000..617517a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/doc_info.py @@ -0,0 +1,297 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# this analyzer leverage a forked version of Oletools ... +# ... that implements additional features to correctly analyze some particular files +# original repository: https://github.com/decalage2/oletools +# forked repository: https://github.com/mlodic/oletools +import logging +import re +import zipfile +from re import sub +from typing import Dict, List + +import olefile +from defusedxml.ElementTree import fromstring +from oletools import mraptor +from oletools.common.clsid import KNOWN_CLSIDS +from oletools.msodde import process_maybe_encrypted as msodde_process_maybe_encrypted +from oletools.olevba import VBA_Parser + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.models import MimeTypes + +logger = logging.getLogger(__name__) + +try: + from XLMMacroDeobfuscator.deobfuscator import show_cells + from XLMMacroDeobfuscator.xls_wrapper_2 import XLSWrapper2 +except Exception as e: + logger.exception(e) + + +class CannotDecryptException(Exception): + pass + + +class DocInfo(FileAnalyzer): + experimental: bool + additional_passwords_to_check: list + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.olevba_results = {} + self.vbaparser = None + self.passwords_to_check = [] + + self.passwords_to_check.extend(self.additional_passwords_to_check) + + def update(self) -> bool: + pass + + def run(self): + results = {} + + # olevba + try: + self.vbaparser = VBA_Parser(self.filepath) + + self.manage_encrypted_doc() + + self.manage_xlm_macros() + + # go on with the normal oletools execution + self.olevba_results["macro_found"] = self.vbaparser.detect_vba_macros() + + if self.olevba_results["macro_found"]: + vba_code_all_modules = "" + macro_data = [] + for ( + v_filename, + stream_path, + vba_filename, + vba_code, + ) in self.vbaparser.extract_macros(): + extracted_macro = { + "filename": v_filename, + "ole_stream": stream_path, + "vba_filename": vba_filename, + "vba_code": vba_code, + } + macro_data.append(extracted_macro) + vba_code_all_modules += vba_code + "\n" + self.olevba_results["macro_data"] = macro_data + + # example output + # + # {'description': 'Runs when the Word document is opened', + # 'keyword': 'AutoOpen', + # 'type': 'AutoExec'}, + # {'description': 'May run an executable file or a system command', + # 'keyword': 'Shell', + # 'type': 'Suspicious'}, + # {'description': 'May run an executable file or a system command', + # 'keyword': 'WScript.Shell', + # 'type': 'Suspicious'}, + # {'description': 'May run an executable file or a system command', + # 'keyword': 'Run', + # 'type': 'Suspicious'}, + # {'description': 'May run PowerShell commands', + # 'keyword': 'powershell', + # 'type': 'Suspicious'}, + # {'description': '9BA55BE5', 'keyword': 'xxx', 'type': 'Hex String'}, + + # mraptor + macro_raptor = mraptor.MacroRaptor(vba_code_all_modules) + if macro_raptor: + macro_raptor.scan() + results["mraptor"] = ( + "suspicious" if macro_raptor.suspicious else "ok" + ) + + # analyze macros + analyzer_results = self.vbaparser.analyze_macros() + # it gives None if it does not find anything + if analyzer_results: + analyze_macro_results = [] + for kw_type, keyword, description in analyzer_results: + if kw_type != "Hex String": + analyze_macro_result = { + "type": kw_type, + "keyword": keyword, + "description": description, + } + analyze_macro_results.append(analyze_macro_result) + self.olevba_results["analyze_macro"] = analyze_macro_results + + results["extracted_CVEs"] = self.analyze_for_cve() + + except CannotDecryptException as e: + logger.info(e) + except Exception as e: + error_message = f"job_id {self.job_id} vba parser failed. Error: {e}" + logger.warning(error_message, stack_info=True) + self.report.errors.append(error_message) + self.report.save() + finally: + if self.vbaparser: + self.vbaparser.close() + + results["olevba"] = self.olevba_results + if self.file_mimetype != MimeTypes.ONE_NOTE.value: + results["msodde"] = self.analyze_msodde() + if self.file_mimetype in [ + MimeTypes.WORD1.value, + MimeTypes.WORD2.value, + MimeTypes.ZIP1.value, + MimeTypes.ZIP2.value, + ]: + results["follina"] = self.analyze_for_follina_cve() + return results + + def analyze_for_follina_cve(self) -> List[str]: + hits = [] + try: + # case docx + zipped = zipfile.ZipFile(self.filepath) + except zipfile.BadZipFile: + logger.info( + f"file {self.filename} is not a zip file so we" + "cant' do custom Follina Extraction" + ) + else: + try: + template = zipped.read("word/_rels/document.xml.rels") + except KeyError: + pass + else: + # logic reference: + # https://github.com/MalwareTech/FollinaExtractor/blob/main/extract_follina.py#L7 + xml_root = fromstring(template) + for xml_node in xml_root.iter(): + target = xml_node.attrib.get("Target") + if target: + target = target.strip().lower() + hits += re.findall(r"mhtml:(https?://.*?)!", target) + return hits + + def analyze_for_cve(self) -> List: + pattern = r"CVE-\d{4}-\d{4,7}" + results = [] + ole = olefile.OleFileIO(self.filepath) + for entry in sorted(ole.listdir(storages=True)): + clsid = ole.getclsid(entry) + if clsid_text := KNOWN_CLSIDS.get(clsid.upper(), None): + if "cve" in clsid_text.lower(): + results.append( + { + "clsid": clsid, + "info": clsid_text, + "CVEs": list(re.findall(pattern, clsid_text)), + } + ) + return results + + def analyze_msodde(self): + try: + msodde_result = msodde_process_maybe_encrypted( + self.filepath, self.passwords_to_check + ) + except Exception as e: + error_message = f"job_id {self.job_id} msodde parser failed. Error: {e}" + # This may happen for text/plain samples types + # and should not be treated as an engine error + if "Could not determine delimiter" in str(e) or self.filename.endswith( + ".exe" + ): + logger.info(error_message, stack_info=True) + else: + logger.warning(error_message, stack_info=True) + self.report.errors.append(error_message) + self.report.save() + msodde_result = f"Error: {e}" + return msodde_result + + def manage_encrypted_doc(self): + self.olevba_results["is_encrypted"] = False + # checks if it is an OLE file. That could be encrypted + if self.vbaparser.ole_file: + # check if the ole file is encrypted + is_encrypted = self.vbaparser.detect_is_encrypted() + self.olevba_results["is_encrypted"] = is_encrypted + # in the case the file is encrypted I try to decrypt it + # with the default password and the most common ones + if is_encrypted: + # by default oletools contains some basic passwords + # we just add some more guesses + common_pwd_to_check = [] + for num in range(10): + common_pwd_to_check.append(f"{num}{num}{num}{num}") + # https://twitter.com/JohnLaTwC/status/1265377724522131457 + filename_without_spaces_and_numbers = sub( + r"[-_\d\s]", "", self.filename + ) + filename_without_extension = sub( + r"(\..+)", "", filename_without_spaces_and_numbers + ) + common_pwd_to_check.append(filename_without_extension) + self.passwords_to_check.extend(common_pwd_to_check) + decrypted_file_name, correct_password = self.vbaparser.decrypt_file( + self.passwords_to_check, + ) + self.olevba_results[ + "additional_passwords_tried" + ] = self.passwords_to_check + if correct_password: + self.olevba_results["correct_password"] = correct_password + if decrypted_file_name: + self.vbaparser = VBA_Parser(decrypted_file_name) + else: + self.olevba_results["cannot_decrypt"] = True + raise CannotDecryptException( + "cannot decrypt the file with the default password" + ) + + def manage_xlm_macros(self): + # this would overwrite classic XLM parsing + self.olevba_results["xlm_macro"] = False + # check if the file contains an XLM macro + # and try an experimental parsing + # credits to https://twitter.com/gabriele_pippi for the idea + if self.vbaparser.detect_xlm_macros(): + self.olevba_results["xlm_macro"] = True + logger.debug("experimental XLM macro analysis start") + parsed_file = b"" + try: + excel_doc = XLSWrapper2(self.filepath) + ae_list = [ + "auto_open", + "auto_close", + "auto_activate", + "auto_deactivate", + ] + self.olevba_results["xlm_macro_autoexec"] = [] + for ae in ae_list: + auto_exec_labels = excel_doc.get_defined_name(ae, full_match=False) + for label in auto_exec_labels: + self.olevba_results["xlm_macro_autoexec"].append(label[0]) + + for i in show_cells(excel_doc): + rec_str = "" + if len(i) == 5: + # rec_str = 'CELL:{:10}, {:20}, {}' + # .format(i[0].get_local_address(), i[2], i[4]) + if i[2] != "None": + rec_str = "{:20}".format(i[2]) + if rec_str: + parsed_file += rec_str.encode() + parsed_file += b"\n" + except Exception as e: + logger.info(f"experimental XLM macro analysis failed. Exception: {e}") + else: + logger.debug( + f"experimental XLM macro analysis succeeded. " + f"Binary to analyze: {parsed_file}" + ) + if parsed_file: + self.vbaparser = VBA_Parser(self.filename, data=parsed_file) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/docguard.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/docguard.py new file mode 100644 index 0000000..a668d05 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/docguard.py @@ -0,0 +1,56 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class DocGuardUpload(FileAnalyzer): + url = "https://api.docguard.io:8443/api" + _api_key_name: str + + def run(self): + headers = {} + if hasattr(self, "_api_key_name"): + headers["x-api-key"] = self._api_key_name + else: + warning = "No API key retrieved" + logger.info( + f"{warning}. Continuing without API key..." f" <- {self.__repr__()}" + ) + self.report.errors.append(warning) + + binary = self.read_file_bytes() + if not binary: + raise AnalyzerRunException("File is empty") + response = requests.post( + self.url + "/FileAnalyzing/AnalyzeFile", + headers=headers, + files={"file": (self.filename, binary)}, + ) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + patch( + "requests.post", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/elf_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/elf_info.py new file mode 100644 index 0000000..65e7618 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/elf_info.py @@ -0,0 +1,52 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging + +from elftools.common.exceptions import ELFError +from elftools.construct import Container +from elftools.elf.elffile import ELFFile +from telfhash import telfhash + +from api_app.analyzers_manager.classes import FileAnalyzer + +logger = logging.getLogger(__name__) + + +class ELFInfo(FileAnalyzer): + @staticmethod + def _convert_to_dict(element): + if type(element) is Container: + return { + key: ELFInfo._convert_to_dict(value) for key, value in element.items() + } + else: + return element + + def run(self): + results = {} + try: + with open(self.filepath, "rb") as file: + elf = ELFFile(file) + if elf is None: + raise ELFError("Not an ELF file") + try: + results["telf"] = telfhash(self.filepath)[0] + except IndexError: + raise ELFError("Not an ELF file") + + results["telf"].pop("file", None) + results["header"] = self._convert_to_dict(elf.header) + results["elfclass"] = elf.elfclass + results["little_endian"] = elf.little_endian + + except ELFError as e: + warning_message = ( + f"job_id:{self.job_id} analyzer:{self.analyzer_name}" + f" md5:{self.md5} filename: {self.filename} ELFError {e}" + ) + logger.warning(warning_message) + self.report.errors.append(warning_message) + self.report.status = self.report.Status.FAILED + self.report.save() + + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/file_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/file_info.py new file mode 100644 index 0000000..f7927b1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/file_info.py @@ -0,0 +1,59 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from pathlib import PosixPath +from typing import Optional + +import magic +import pydeep +import tlsh +from django.conf import settings +from django.utils.functional import cached_property +from exiftool import ExifTool + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.helpers import calculate_md5, calculate_sha1, calculate_sha256 + +logger = logging.getLogger(__name__) + + +class FileInfo(FileAnalyzer): + EXIF_TOOL_PATH: PosixPath = settings.BASE_DIR / "exiftool_download" + EXIF_TOOL_VERSION_PATH: PosixPath = EXIF_TOOL_PATH / "exiftool_version.txt" + + @cached_property + def exiftool_path(self) -> Optional[str]: + # check repo_downloader.sh file + if not self.EXIF_TOOL_VERSION_PATH.exists(): + return None + with open(self.EXIF_TOOL_VERSION_PATH, "r", encoding="utf-8") as f: + version = f.read().strip() + return f"{self.EXIF_TOOL_PATH}/Image-ExifTool-{version}/exiftool" + + def run(self): + results = {} + results["magic"] = magic.from_file(self.filepath) + results["mimetype"] = magic.from_file(self.filepath, mime=True) + + binary = self.read_file_bytes() + results["md5"] = calculate_md5(binary) + results["sha1"] = calculate_sha1(binary) + results["sha256"] = calculate_sha256(binary) + results["ssdeep"] = pydeep.hash_file(self.filepath).decode() + results["tlsh"] = tlsh.hash(binary) + + if self.exiftool_path: + with ExifTool(self.exiftool_path) as et: + exif_report = et.execute_json(self.filepath) + if exif_report: + exif_single_report = exif_report[0] + exif_report_cleaned = { + key: value + for key, value in exif_single_report.items() + if not (key.startswith("File") or key.startswith("SourceFile")) + } + # compatibility with the previous version of this analyzer + results["filetype"] = exif_single_report.get("File:FileType", "") + results["exiftool"] = exif_report_cleaned + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/filescan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/filescan.py new file mode 100644 index 0000000..eb99ad7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/filescan.py @@ -0,0 +1,84 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class FileScanUpload(FileAnalyzer): + """FileScan_Upload_File analyzer""" + + max_tries: int = 30 + poll_distance: int = 10 + url = "https://www.filescan.io/api" + _api_key: str + + def run(self): + task_id = self.__upload_file_for_scan() + report = self.__fetch_report(task_id) + return report + + def __upload_file_for_scan(self) -> int: + binary = self.read_file_bytes() + if not binary: + raise AnalyzerRunException("File is empty") + response = requests.post( + self.url + "/scan/file", + files={"file": (self.filename, binary)}, + headers={"X-Api-Key": self._api_key}, + ) + response.raise_for_status() + + return response.json()["flow_id"] + + def __fetch_report(self, task_id: int) -> dict: + report = {} + url = f"{self.url}/scan/{task_id}/report" + params = { + "filter": [ + "general", + "wi:all", + "o:all", + "finalVerdict", + "dr:all", + "f:all", + "fd:all", + ] + } + obj_repr = self.__repr__() + + for chance in range(self.max_tries): + logger.info(f"[POLLING] {obj_repr} -> #{chance + 1}/{self.max_tries}") + response = requests.get( + url, params=params, headers={"X-Api-Key": self._api_key} + ) + report = response.json() + if report["allFinished"]: + break + time.sleep(self.poll_distance) + + return report + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"allFinished": True}, 200), + ), + patch( + "requests.post", + return_value=MockUpResponse({"flow_id": 1}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/floss.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/floss.py new file mode 100644 index 0000000..cc6d5a9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/floss.py @@ -0,0 +1,74 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from json import dumps as json_dumps + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + + +class Floss(FileAnalyzer, DockerBasedAnalyzer): + name: str = "Floss" + url: str = "http://malware_tools_analyzers:4002/floss" + ranking_url: str = "http://malware_tools_analyzers:4002/stringsifter" + # interval between http request polling + poll_distance: int = 10 + # http request polling max number of tries + max_tries: int = 60 + # here, max_tries * poll_distance = 10 minutes + # whereas subprocess timeout is kept as 60 * 9 = 9 minutes + timeout: int = 60 * 9 + # this is retrieved with bash command `getconf ARG_MAX` + OS_MAX_ARGS: int = 2097152 + + max_no_of_strings: dict + rank_strings: dict + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + # get binary + binary = self.read_file_bytes() + # make request data + fname = str(self.filename).replace("/", "_").replace(" ", "_") + # From floss v3 there is prompt that can be overcome + # by using the flag --no static. + # We can lose static strings considering that we can easily + # retrieve them with more simple tools + args = [f"@{fname}", "--json", "--no", "static"] + req_data = {"args": args, "timeout": self.timeout} + req_files = {fname: binary} + result = self._docker_run(req_data, req_files) + if not isinstance(result, dict): + raise AnalyzerRunException( + f"result from floss tool is not a dict but is {type(result)}." + f" Full dump: {result}" + ) + result["exceeded_max_number_of_strings"] = {} + # we are changing the endpoint of _docker_run to stringsifter + self.url = self.ranking_url + + for key in self.max_no_of_strings: + if self.rank_strings[key]: + strings = json_dumps(result["strings"][key]) + # 4 is the number of arguments that we are already passing + analyzable_strings = strings[: self.OS_MAX_ARGS - 5] + args = [ + "rank_strings", + "--limit", + str(self.max_no_of_strings[key]), + "--strings", + analyzable_strings, + ] + req_data = {"args": args, "timeout": self.timeout} + result["strings"][key] = self._docker_run(req_data) + else: + if ( + len(result.get("strings", {}).get(key, [])) + > self.max_no_of_strings[key] + ): + result["strings"][key] = list(result["strings"][key]) + result["exceeded_max_number_of_strings"][key] = True + return result diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/hfinger.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/hfinger.py new file mode 100644 index 0000000..d1c6ed9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/hfinger.py @@ -0,0 +1,67 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from hfinger.analysis import hfinger_analyze + +from api_app.analyzers_manager.classes import FileAnalyzer +from tests.mock_utils import if_mock_connections, patch + + +class Hfinger(FileAnalyzer): + """ + Create fingerprints of malware HTTP + requests stored in pcap files. + """ + + fingerprint_report_mode: int = 2 + + def run(self): + reports = dict() + reports["extraction"] = hfinger_analyze( + self.filepath, self.fingerprint_report_mode + ) + fingerprints = set() + for report in reports["extraction"]: + fingerprint = report.get("fingerprint", "") + if fingerprint: + fingerprints.add(fingerprint) + reports["fingerprints_summary"] = list(fingerprints) + return reports + + @classmethod + def update(cls) -> bool: + pass + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "hfinger.analysis.hfinger_analyze", + return_value=[ + { + "epoch_time": "1388111476.787707000", + "ip_src": "192.168.1.138", + "ip_dst": "173.194.115.80", + "port_src": "49209", + "port_dst": "80", + "fingerprint": "2.4|1|0.5||2.4|1.2|GE|1|ac,ac-la,us-ag,\ + ac-en,ho,co|ac:te-ht,ap-xh+xm,as-as/ac-la:75ef792f/\ + us-ag:ca0c4d71/ac-en:gz,de/co:Ke-Al|||", + }, + { + "epoch_time": "1388111477.142485000", + "ip_src": "192.168.1.138", + "ip_dst": "66.225.230.141", + "port_src": "49220", + "port_dst": "80", + "fingerprint": "1.5|3|1.0|html|||GE|1|ac,re,ac-la,us-ag,\ + ac-en,ho,co|ac:te-ht,ap-xh+xm,as-as/ac-la:75ef792f/\ + us-ag:ca0c4d71/ac-en:gz,de/co:Ke-Al|||", + }, + ], + ) + ) + ] + + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/intezer_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/intezer_scan.py new file mode 100644 index 0000000..9e74c51 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/intezer_scan.py @@ -0,0 +1,79 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from datetime import timedelta +from typing import Dict + +import intezer_sdk.consts +from intezer_sdk import api as intezer_api +from intezer_sdk import errors as intezer_errors +from intezer_sdk.analysis import FileAnalysis + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import if_mock_connections, patch + + +class IntezerScan(FileAnalyzer): + soft_time_limit: int + disable_dynamic_unpacking: bool + disable_static_unpacking: bool + _api_key_name: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + # soft time limit + self.timeout = self.soft_time_limit - 5 + # interval + self.poll_interval = 3 + + self.upload_file = self._job.tlp == self._job.TLP.CLEAR.value + + intezer_api.set_global_api(api_key=self._api_key_name) + + def run(self): + result = {} + + try: + intezer_sdk.consts.USER_AGENT = "IntelOwl" + # run analysis by hash + hash_result = self.__intezer_analysis(file_hash=self.md5) + result.update(hash_result, hash_found=True) + except intezer_errors.HashDoesNotExistError: + result.update(hash_found=False) + if self.upload_file: + # run analysis by file + file_result = self.__intezer_analysis( + file_stream=self.read_file_bytes() + ) + result.update(file_result, hash_found=False) + except intezer_errors.IntezerError as e: + raise AnalyzerRunException(e) + + return result + + def __intezer_analysis(self, **kwargs) -> dict: + analysis = FileAnalysis( + **kwargs, + disable_dynamic_unpacking=self.disable_dynamic_unpacking, + disable_static_unpacking=self.disable_static_unpacking, + file_name=self.filename, + ) + analysis.send(wait=False) + analysis.wait_for_completion( + interval=self.poll_interval, + sleep_before_first_check=True, + timeout=timedelta(seconds=self.timeout), + ) + return analysis.result() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch.object(FileAnalysis, "send", return_value=None), + patch.object(FileAnalysis, "wait_for_completion", return_value=None), + patch.object(FileAnalysis, "result", return_value={"test": "test"}), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/malpedia_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/malpedia_scan.py new file mode 100644 index 0000000..a769740 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/malpedia_scan.py @@ -0,0 +1,42 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class MalpediaScan(FileAnalyzer): + """ + Scan a binary against all YARA rules in Malpedia. + """ + + url = "https://malpedia.caad.fkie.fraunhofer.de/api" + binary_url = url + "/scan/binary" + + _api_key_name: str + + def run(self): + # get file + binary = self.read_file_bytes() + # construct req + headers = {"Authorization": f"APIToken {self._api_key_name}"} + files = {"file": binary} + response = requests.post(self.binary_url, headers=headers, files=files) + response.raise_for_status() + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse({}, 200), + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/mwdb_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/mwdb_scan.py new file mode 100644 index 0000000..9a1ebb0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/mwdb_scan.py @@ -0,0 +1,136 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time +from typing import Dict + +import mwdblib +from requests import HTTPError + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MagicMock, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +def mocked_mwdb_response(*args, **kwargs): + attrs = { + "data": {"id": "id_test", "children": [], "parents": []}, + "attributes": {"karton": "test_analysis"}, + } + fileInfo = MagicMock() + fileInfo.configure_mock(**attrs) + QueryResponse = MagicMock() + attrs = {"query_file.return_value": fileInfo} + QueryResponse.configure_mock(**attrs) + Response = MagicMock(return_value=QueryResponse) + return Response.return_value + + +class MockUpUploadObject: + def __init__(self): + self.data = {"id": "id_test"} + + def flush(self): + return + + +class MockUpQueryObject: + def __init__(self): + self.attributes = {"karton": "test"} + self.data = {"children": [], "parents": []} + + +class MockUpMWDB: + def __init__(self): + pass + + @staticmethod + def upload_file(*args, **kwargs): + return MockUpUploadObject() + + @staticmethod + def query_file(*args, **kwargs): + return MockUpQueryObject() + + +class MWDB_Scan(FileAnalyzer): + _api_key_name: str + private: bool + max_tries: int + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.public = not self.private + self.poll_distance = 5 + self.upload_file = self._job.tlp == self._job.TLP.CLEAR.value + + def adjust_relations(self, base, key, recursive=True): + new_relation = [] + for relation in base[key]: + if relation["type"] == "file": + new_relation.append(self.mwdb.query_file(relation["id"]).data) + elif relation["type"] == "static_config": + new_relation.append(self.mwdb.query_config(relation["id"]).data) + base[key] = new_relation + # HERE WE GO + if recursive: + for new_base in base[key]: + if base["type"] == "file": + # otherwise we have an infinite loop + if key == "parents": + self.adjust_relations(new_base, key="parents", recursive=True) + self.adjust_relations(new_base, key="children", recursive=False) + elif key == "children": + self.adjust_relations(new_base, key="parents", recursive=True) + self.adjust_relations(new_base, key="children", recursive=False) + + def run(self): + result = {} + binary = self.read_file_bytes() + query = self._job.sha256 + self.mwdb = mwdblib.MWDB(api_key=self._api_key_name) + + if self.upload_file: + logger.info(f"mwdb_scan uploading sample: {self.md5}") + file_object = self.mwdb.upload_file( + query, binary, private=self.private, public=self.public + ) + file_object.flush() + for _try in range(self.max_tries): + logger.info( + f"mwdb_scan sample: {self.md5} polling for result try #{_try + 1}" + ) + file_info = self.mwdb.query_file(file_object.data["id"]) + if "karton" in file_info.attributes.keys(): + break + time.sleep(self.poll_distance) + else: + raise AnalyzerRunException("max retry attempts exceeded") + else: + try: + file_info = self.mwdb.query_file(query) + except HTTPError: + result["not_found"] = True + return result + else: + result["not_found"] = False + # adding information about the children and parents + self.adjust_relations(file_info.data, "parents", True) + self.adjust_relations(file_info.data, "children", True) + + result.update( + data=file_info.data, + permalink=f"https://mwdb.cert.pl/file/{query}", + ) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections(patch("mwdblib.MWDB", return_value=MockUpMWDB())) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/onenote.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/onenote.py new file mode 100644 index 0000000..ab71c7f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/onenote.py @@ -0,0 +1,18 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +import logging + +from pyOneNote.Main import process_onenote_file + +from api_app.analyzers_manager.classes import FileAnalyzer + +logger = logging.getLogger(__name__) + + +class OneNoteInfo(FileAnalyzer): + def run(self): + with open(self.filepath, "rb") as file: + results = json.loads(process_onenote_file(file, "", "", True)) + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/pdf_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/pdf_info.py new file mode 100644 index 0000000..20cd29e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/pdf_info.py @@ -0,0 +1,81 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from typing import Any, List + +import peepdf +from pdfid import pdfid + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + +logger = logging.getLogger(__name__) + + +class PDFInfo(FileAnalyzer): + @staticmethod + def flatten(list_of_lists: List[List[Any]]) -> List[Any]: + return [item for sublist in list_of_lists for item in sublist] + + def run(self): + self.results = {"peepdf": {}, "pdfid": {}} + # the analysis fails only when BOTH fails + peepdf_success = self.__peepdf_analysis() + pdfid_success = self.__pdfid_analysis() + if not peepdf_success and not pdfid_success: + raise AnalyzerRunException("both peepdf and pdfid failed") + return self.results + + def __peepdf_analysis(self): + success = False + peepdf_analysis = {} + try: + pdf_parser = peepdf.PDFCore.PDFParser() + ret, pdf = pdf_parser.parse(self.filepath, True) + if ret: + peepdf_analysis["status_code"] = ret + else: + stats = pdf.getStats() + peepdf_analysis["stats"] = [] + for version in stats.get("Versions", []): + version_dict = { + "events": version.get("Events", {}), + "actions": version.get("Actions", {}), + "urls": self.flatten(pdf.getURLs()), + "uris": self.flatten(pdf.getURIs()), + "elements": version.get("Elements", {}), + "vulns": version.get("Vulns", []), + "objects_with_js_code": version.get("Objects with JS code", []), + } + peepdf_analysis["stats"].append(version_dict) + + self.results["peepdf"] = peepdf_analysis + except TypeError as e: + # rc4Key = rc4Key[:keyLength] + # TypeError: slice indices must be integers or None or + # have an __index__ method + self.results["peepdf"]["error"] = str(e) + except UnboundLocalError as e: + logger.info(e, stack_info=True) + self.results["peepdf"]["error"] = str(e) + except Exception as e: + logger.exception(e) + self.results["peepdf"]["error"] = str(e) + else: + success = True + return success + + def __pdfid_analysis(self): + success = False + try: + options = pdfid.get_fake_options() + options.json = True + list_of_dict = pdfid.PDFiDMain([self.filepath], options) + self.results["pdfid"] = list_of_dict + except Exception as e: + logger.exception(e) + self.results["pdfid"]["error"] = str(e) + else: + success = True + return success diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/pe_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/pe_info.py new file mode 100644 index 0000000..77ec9b1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/pe_info.py @@ -0,0 +1,207 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# this analyzer leverage a forked version of PEfile ... +# ... that fixes one common problem encountered in a lot of analysis +# original repository: https://github.com/erocarrera/pefile +# forked repository: https://github.com/mlodic/pefile + +import logging +import os +from datetime import datetime + +import lief +import pefile +import pyimpfuzzy +from PIL import Image + +from api_app.analyzers_manager.classes import FileAnalyzer + +logger = logging.getLogger(__name__) + + +class NT_Header_Error(Exception): + """ + Class that resembles a NT Header exception (invalid PE File) for WinPEhasher + """ + + +class No_Icon_Error(Exception): + """ + Class that resembles a No Icon resource exception for WinPEhasher dhashicon + """ + + +class PEInfo(FileAnalyzer): + def run(self): + results = {} + try: + pe = pefile.PE(self.filepath) + if not pe: + raise pefile.PEFormatError("Empty file?") + full_dump = pe.dump_dict() + + results["imphash"] = pe.get_imphash() + + results["warnings"] = pe.get_warnings() + + if pe.is_dll(): + results["type"] = "DLL" + elif pe.is_driver(): + results["type"] = "DRIVER" + elif pe.is_exe(): + results["type"] = "EXE" + + sections = [] + for section in pe.sections: + try: + name = section.Name.decode() + except UnicodeDecodeError as e: + name = "UnableToDecode" + logger.warning( + f"Unable to decode section {section.Name} exception {e}" + ) + section_item = { + "name": name, + "address": hex(section.VirtualAddress), + "virtual_size": hex(section.Misc_VirtualSize), + "size": section.SizeOfRawData, + "entropy": section.get_entropy(), + } + sections.append(section_item) + + results["sections"] = sections + + machine_value = pe.FILE_HEADER.Machine + results["machine"] = machine_value + mt = {"0x14c": "x86", "0x0200": "Itanium", "0x8664": "x64"} + architecture = "" + if isinstance(machine_value, int): + architecture = mt.get(str(hex(machine_value)), "") + if not architecture: + architecture = str(machine_value) + " => Not x86/64 or Itanium" + results["architecture"] = architecture + + results["os"] = ( + f"{pe.OPTIONAL_HEADER.MajorOperatingSystemVersion}" + f".{pe.OPTIONAL_HEADER.MinorOperatingSystemVersion}" + ) + + results["dhashicon_hash"] = self._dhashicon() + results["impfuzzy_hash"] = self._impfuzzy() + + results["entrypoint"] = hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint) + + results["imagebase"] = hex(pe.OPTIONAL_HEADER.ImageBase) + + timestamp = pe.FILE_HEADER.TimeDateStamp + results["compilation_timestamp"] = datetime.utcfromtimestamp( + timestamp + ).strftime("%Y-%m-%d %H:%M:%S") + + import_table = [] + directory_entry_import = getattr(pe, "DIRECTORY_ENTRY_IMPORT", []) + for entry in directory_entry_import: + imp = { + "entryname": entry.dll.decode() if entry.dll else None, + "symbols": [], + } + for symbol in entry.imports: + if symbol.name: + imp["symbols"].append(symbol.name.decode()) + import_table.append(imp) + results["import_table"] = import_table + + export_table = [] + for entry in full_dump.get("Exported symbols", []): + symbol_name = entry.get("Name", None) + # in case it is a dictionary, we do not mind it + try: + export_table.append(symbol_name.decode()) + except (UnicodeDecodeError, AttributeError) as e: + logger.debug( + f"PE info error while decoding export table symbols: {e}" + ) + # this is to reduce the output + export_table = export_table[:100] + results["export_table"] = export_table + + results["flags"] = full_dump.get("Flags", []) + + except pefile.PEFormatError as e: + warning_message = ( + f"job_id:{self.job_id} analyzer:{self.analyzer_name}" + f" md5:{self.md5} filename: {self.filename} PEFormatError {e}" + ) + logger.warning(warning_message) + self.report.errors.append(warning_message) + self.report.status = self.report.Status.FAILED + self.report.save() + + return results + + def _dhashicon(self) -> str: + """ + Return Dhashicon of the exe + """ + # this code was implemented from + # https://github.com/fr0gger/SuperPeHasher/commit/e9b753bf52d4e48dda2da0b7801075be4037a161#diff-2bb2d2d4d25fef20a893a4e93e96c9b8c0b0c6d5791fc14594cc9dd5cbf40b41 + # config + dhashicon = None + try: + hash_size = 8 + # extract icon + icon_path = self.filepath + ".ico" + binary = lief.parse(self.filepath) + if binary is None: + # Invalid PE file + raise NT_Header_Error("binary is None") + # extracting icon and saves in a temp file + binres = binary.resources_manager + if not binres.has_type(lief.PE.RESOURCE_TYPES.ICON): + # no icon resources in file + raise No_Icon_Error("no icon resource in file") + ico = binres.icons + ico[0].save(icon_path) + # resize + exe_icon = Image.open(icon_path) + exe_icon = exe_icon.convert("L").resize( + (hash_size + 1, hash_size), Image.ANTIALIAS + ) + diff = [] + for row in range(hash_size): + for col in range(hash_size): + left = exe_icon.getpixel((col, row)) + right = exe_icon.getpixel((col + 1, row)) + diff.append(left > right) + decimal_value = 0 + icon_hash = [] + for index, value in enumerate(diff): + if value: + decimal_value += 2 ** (index % 8) + if (index % 8) == 7: + icon_hash.append(hex(decimal_value)[2:].rjust(2, "0")) + decimal_value = 0 + os.remove(icon_path) + dhashicon = "".join(icon_hash) + except Exception as e: + error = str(e) + if not error or "resource" in error: + logger.info(e, stack_info=True) + else: + logger.warning(e, stack_info=True) + return dhashicon + + def _impfuzzy(self): + """ + Calculate impfuzzy hash and return + """ + impfuzzyhash = None + try: + # this code was implemented from + # https://github.com/JPCERTCC/impfuzzy + # + impfuzzyhash = str(pyimpfuzzy.get_impfuzzy(self.filepath)) + except Exception as e: + logger.warning(e, stack_info=True) + return impfuzzyhash diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/peframe.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/peframe.py new file mode 100644 index 0000000..d4ef9fd --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/peframe.py @@ -0,0 +1,32 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class PEframe(FileAnalyzer, DockerBasedAnalyzer): + name: str = "PEframe" + url: str = "http://malware_tools_analyzers:4002/peframe" + # http request polling max number of tries + max_tries: int = 25 + # interval between http request polling + poll_distance: int = 5 + timeout: int = 60 * 9 + # whereas subprocess timeout is kept as 60 * 9 = 9 minutes + + def run(self): + # get binary + binary = self.read_file_bytes() + # make request data + fname = str(self.filename).replace("/", "_").replace(" ", "_") + req_data = {"args": ["-j", f"@{fname}"], "timeout": self.timeout} + req_files = {fname: binary} + + result = self._docker_run(req_data, req_files) + + if result: + # limit strings dump to first 100 + if "strings" in result and "dump" in result["strings"]: + result["strings"]["dump"] = result["strings"]["dump"][:100] + + return result diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/perm_hash.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/perm_hash.py new file mode 100644 index 0000000..b729c78 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/perm_hash.py @@ -0,0 +1,84 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +import magic +from permhash.functions import ( + APK_MANIFEST_MIMETYPES, + APK_MIMETYPES, + CRX_MANIFEST_MIMETYPES, + CRX_MIMETYPES, + permhash_apk, + permhash_apk_manifest, + permhash_crx, + permhash_crx_manifest, +) + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Permhash(FileAnalyzer): + """ + Create permissions hash of APK, Chrome extensions, + Android manifest and Chrome extension manifest files. + """ + + def run(self): + result = {} + mimetype = magic.from_file(self.filepath, mime=True) + + hash_val = "" + + if mimetype in APK_MIMETYPES: + hash_val = permhash_apk(self.filepath) + elif mimetype in APK_MANIFEST_MIMETYPES: + hash_val = permhash_apk_manifest(self.filepath) + elif mimetype in CRX_MIMETYPES: + hash_val = permhash_crx(self.filepath) + elif mimetype in CRX_MANIFEST_MIMETYPES: + hash_val = permhash_crx_manifest(self.filepath) + else: + raise AnalyzerRunException(f"Mimetype {mimetype} not supported.") + + # permhash returns False if for some reason the hash value can't be found + if hash_val: + result["hash"] = hash_val + else: + result["error"] = "Could not find permissions in the file." + + return result + + @classmethod + def update(cls) -> bool: + pass + + @classmethod + def _monkeypatch(cls): + hash_val = "aad106ceb64ac2a636ddec77c3feed4c2ffc5c27ab353660d8cb3e1c971ef278" + patches = [ + if_mock_connections( + patch( + "permhash.functions.permhash_apk", + return_value=hash_val, + ), + patch( + "permhash.functions.permhash_apk_manifest", + return_value=hash_val, + ), + patch( + "permhash.functions.permhash_crx", + return_value=hash_val, + ), + patch( + "permhash.functions.permhash_crx_manifest", + return_value=hash_val, + ), + ) + ] + + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/qiling.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/qiling.py new file mode 100644 index 0000000..5b4913e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/qiling.py @@ -0,0 +1,44 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + + +class Qiling(FileAnalyzer, DockerBasedAnalyzer): + name: str = "Qiling" + url: str = "http://malware_tools_analyzers:4002/qiling" + # http request polling max number of tries + max_tries: int = 15 + # interval between http request polling (in secs) + poll_distance: int = 30 + timeout: int = 60 * 9 + # whereas subprocess timeout is kept as 60 * 9 = 9 minutes + + os: str + arch: str + shellcode: bool + profile: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.args = [self.os, self.arch] + if self.shellcode: + self.args.append("--shellcode") + if self.profile: + self.args.extend(["--profile"] + [self.profile]) + + def run(self): + # get the file to send + fname = str(self.filename).replace("/", "_").replace(" ", "_") + binary = self.read_file_bytes() + # make request parameters + req_data = {"args": [f"@{fname}", *self.args], "timeout": self.timeout} + req_files = {fname: binary} + report = self._docker_run(req_data, req_files) + if report.get("setup_error"): + raise AnalyzerRunException(report["setup_error"]) + if report.get("execution_error"): + raise AnalyzerRunException(report["execution_error"]) + return report diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/quark_engine.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/quark_engine.py new file mode 100644 index 0000000..defd91b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/quark_engine.py @@ -0,0 +1,29 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + + +class QuarkEngine(FileAnalyzer): + @classmethod + def update(cls) -> bool: + from quark import freshquark + + # the rules are installed in config.HOME_DIR by default + freshquark.download() + return True + + def run(self): + from quark.config import DIR_PATH + from quark.report import Report + + report = Report() + # start analysis + report.analysis(self.filepath, DIR_PATH) + # return json report + json_report = report.get_report("json") + if not json_report: + raise AnalyzerRunException("json report can not be empty") + return json_report diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/rtf_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/rtf_info.py new file mode 100644 index 0000000..9c91c3d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/rtf_info.py @@ -0,0 +1,56 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import re +from typing import List + +from oletools.rtfobj import RtfObjParser + +from api_app.analyzers_manager.classes import FileAnalyzer + + +class RTFInfo(FileAnalyzer): + def analyze_for_follina_cve(self) -> List[str]: + # logic reference: + # https://github.com/MalwareTech/FollinaExtractor/blob/main/extract_follina.py#L23 + content = self.read_file_bytes().decode("utf8", errors="ignore") + return re.findall(r"objclass (https?://.*?)}", content) + + def run(self): + results = {} + rtfobj_results = {} + binary = self.read_file_bytes() + + rtfp = RtfObjParser(binary) + rtfp.parse() + rtfobj_results["ole_objects"] = [] + for rtfobj in rtfp.objects: + if rtfobj.is_ole: + class_name = rtfobj.class_name.decode() + ole_dict = { + "format_id": rtfobj.format_id, + "class_name": class_name, + "ole_datasize": rtfobj.oledata_size, + } + if rtfobj.is_package: + ole_dict["is_package"] = True + ole_dict["filename"] = rtfobj.filename + ole_dict["src_path"] = rtfobj.src_path + ole_dict["temp_path"] = rtfobj.temp_path + ole_dict["olepkgdata_md5"] = rtfobj.olepkgdata_md5 + else: + ole_dict["ole_md5"] = rtfobj.oledata_md5 + if rtfobj.clsid: + ole_dict["clsid_desc"] = rtfobj.clsid_desc + ole_dict["clsid_id"] = rtfobj.clsid + rtfobj_results["ole_objects"].append(ole_dict) + # http://www.kb.cert.org/vuls/id/921560 + if class_name == "OLE2Link": + rtfobj_results["exploit_ole2link_vuln"] = True + # https://www.kb.cert.org/vuls/id/421280/ + elif class_name.lower() == "equation.3": + rtfobj_results["exploit_equation_editor"] = True + + results["rtfobj"] = rtfobj_results + results["follina"] = self.analyze_for_follina_cve() + + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/signature_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/signature_info.py new file mode 100644 index 0000000..9a746b4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/signature_info.py @@ -0,0 +1,67 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from subprocess import DEVNULL, PIPE, Popen + +from celery.exceptions import SoftTimeLimitExceeded +from django.conf import settings + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + +logger = logging.getLogger(__name__) + + +class SignatureInfo(FileAnalyzer): + def run(self): + p = None + results = { + "checksum_mismatch": False, + "no_signature": False, + "verified": False, + "corrupted": False, + "certificate_has_expired": False, + } + try: + command = [ + f"{settings.PROJECT_LOCATION}/docker/bin/osslsigncode", + "verify", + self.filepath, + ] + p = Popen(command, stdin=DEVNULL, stdout=PIPE, stderr=PIPE) + (out, err) = p.communicate() + output = out.decode() + + if p.returncode == 1: + if "MISMATCH" in output: + results["checksum_mismatch"] = True + # new versions (>=2.0) provide this status code + # when the signature is not found + elif "No signature found" in output: + results["no_signature"] = True + elif p.returncode != 0: + raise AnalyzerRunException( + f"osslsigncode return code is {p.returncode}. Error: {err}" + ) + + if output: + if "No signature found" in output: + results["no_signature"] = True + if "Signature verification: ok" in output: + results["verified"] = True + if "Corrupt PE file" in output: + results["corrupted"] = True + if "certificate has expired" in output: + results["certificate_has_expired"] = True + else: + raise AnalyzerRunException("osslsigncode gave no output?") + + # we should stop the subprocesses... + # .. in case we reach the time limit for the celery task + except SoftTimeLimitExceeded as exc: + self._handle_exception(exc) + if p: + p.kill() + + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/speakeasy_emulation.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/speakeasy_emulation.py new file mode 100644 index 0000000..d74a4f5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/speakeasy_emulation.py @@ -0,0 +1,32 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +import speakeasy +import speakeasy.winenv.arch as e_arch + +from api_app.analyzers_manager.classes import FileAnalyzer + +logger = logging.getLogger(__name__) + + +class SpeakEasy(FileAnalyzer): + raw_offset: int + arch: str + shellcode: bool + + def run(self): + s = speakeasy.Speakeasy() + if self.shellcode: + arch = e_arch.ARCH_AMD64 + if self.arch == "x86": + arch = e_arch.ARCH_X86 + sc_addr = s.load_shellcode(self.filepath, arch) + s.run_shellcode(sc_addr, offset=self.raw_offset or 0) + else: + m = s.load_module(self.filepath) + s.run_module(m) + results = s.get_report() + + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/strings_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/strings_info.py new file mode 100644 index 0000000..427fbcc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/strings_info.py @@ -0,0 +1,55 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from json import dumps as json_dumps + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class StringsInfo(FileAnalyzer, DockerBasedAnalyzer): + name: str = "StringsInfo" + url: str = "http://malware_tools_analyzers:4002/stringsifter" + # interval between http request polling + poll_distance: int = 10 + # http request polling max number of tries + max_tries: int = 60 + # here, max_tries * poll_distance = 10 minutes + timeout: int = 60 * 9 + # whereas subprocess timeout is kept as 60 * 9 = 9 minutes + + max_number_of_strings: int + max_characters_for_string: int + # If set, this module will use Machine Learning feature + # CARE!! ranked_strings could be cpu/ram intensive and very slow + rank_strings: int + + def run(self): + # get binary + binary = self.read_file_bytes() + # make request data + fname = str(self.filename).replace("/", "_").replace(" ", "_") + args = ["flarestrings", f"@{fname}"] + req_data = { + "args": args, + "timeout": self.timeout, + } + req_files = {fname: binary} + result = self._docker_run(req_data, req_files) + exceed_max_strings = len(result) > self.max_number_of_strings + if exceed_max_strings: + result = list(result[: self.max_number_of_strings]) + if self.rank_strings: + args = [ + "rank_strings", + "--limit", + str(self.max_number_of_strings), + "--strings", + json_dumps(result), + ] + req_data = {"args": args, "timeout": self.timeout} + result = self._docker_run(req_data) + result = { + "data": [row[: self.max_characters_for_string] for row in result], + "exceeded_max_number_of_strings": exceed_max_strings, + } + return result diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/sublime.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/sublime.py new file mode 100644 index 0000000..aad0d74 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/sublime.py @@ -0,0 +1,236 @@ +import base64 +import email +from email.message import Message +from logging import getLogger +from typing import Dict +from unittest.mock import patch + +import requests +from django.utils.functional import cached_property + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from api_app.analyzers_manager.models import MimeTypes +from tests.mock_utils import MockUpResponse, if_mock_connections + +logger = getLogger(__name__) + + +class Sublime(FileAnalyzer): + _api_key: str + _message_source_id: str + _url: str + analyze_internal_eml_on_pec: bool + headers = {"accept": "application/json", "content-type": "application/json"} + live_flow_endpoint = "/v1/live-flow/raw-messages/analyze" + retrieve_message_endpoint = "/v0/messages" + api_port = 8000 + gui_port = 3000 + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self._url.endswith("/"): + self._url = self._url[:-1] + + @cached_property + def email(self) -> Message: + return email.message_from_bytes(self.read_file_bytes()) + + def is_pec(self) -> bool: + found_eml, found_signature, found_xml = False, False, False + payload = self.email.get_payload() + if isinstance(payload, str): + return False + for attachment in payload: + attachment: Message + content_type = attachment.get_content_type() + filename = attachment.get_filename() + logger.debug(f"{content_type=} {filename=} ") + if content_type == MimeTypes.MIXED.value: + for internal_attachment in attachment.get_payload(): + internal_attachment: Message + internal_attachment_content = internal_attachment.get_content_type() + internal_attachment_name = internal_attachment.get_filename() + logger.debug( + f"{internal_attachment_name=} {internal_attachment_content=} " + ) + if ( + internal_attachment_content == MimeTypes.EML.value + and internal_attachment_name == "postacert.eml" + ): + self._real_email = attachment.get_payload(0).as_bytes() + found_eml = True + elif ( + internal_attachment_content + in [MimeTypes.XML1.value, MimeTypes.XML2.value] + and internal_attachment_name == "daticert.xml" + ): + found_xml = True + elif ( + content_type in [MimeTypes.PKCS7.value, MimeTypes.XPKCS7.value] + and filename == "smime.p7s" + ): + found_signature = True + logger.debug(f"{found_xml=} {found_eml=} {found_signature=}") + return found_eml and found_signature and found_xml + + @property + def raw_message(self) -> str: + if self.file_mimetype == MimeTypes.OUTLOOK.value: + import subprocess + import tempfile + + with tempfile.NamedTemporaryFile() as file: + command = ["msgconvert", file.name, "--outfile", "-"] + file.seek(0) + file.write(self.read_file_bytes()) + file.seek(0) + proc = subprocess.run(command, check=True, stdout=subprocess.PIPE) + return base64.b64encode(proc.stdout.strip()).decode("utf-8") + return self._job.b64 + + def _analysis(self, session: requests.Session, content: str): + result = session.post( + f"{self._url}:{self.api_port}{self.live_flow_endpoint}", + json={ + "create_mailbox": True, + "raw_message": content, + "message_source_id": self._message_source_id, + "mailbox_email_address": self._job.user.email, + "labels": [self._job.user.username], + "run_active_detection_rules": True, + "run_all_detection_rules": False, + }, + timeout=50, + ) + try: + result.raise_for_status() + except requests.exceptions.RequestException: + raise AnalyzerRunException(result.content) + else: + result_analysis = result.json() + logger.info(f"Result is {result_analysis}") + result_message = session.get( + f"{self._url}:{self.api_port}{self.retrieve_message_endpoint}/" + f"{result_analysis['message_id']}", + timeout=20, + ) + try: + result_message.raise_for_status() + except requests.exceptions.RequestException: + self.report.errors.append(result_message.content) + raise AnalyzerRunException(result_message.content) + else: + result_message = result_message.json() + canonical_id = result_message["canonical_id"] + logger.info(f"{result_message=}") + return { + "flagged_rules": [ + { + key: rule[key] + for key in [ + "name", + "description", + "severity", + "maturity", + "label", + "tags", + "false_positives", + "references", + "updated_at", + "authors", + ] + } + for rule in result_analysis["flagged_rules"] + ], + "gui_url": f"{self._url}:{self.gui_port}/messages/{canonical_id}", + **{ + key: result_message[key] + for key in ["subject", "sender", "recipients", "created_at"] + }, + } + + def run(self) -> Dict: + self.headers["Authorization"] = f"Bearer {self._api_key}" + session = requests.Session() + session.headers = self.headers + report = self._analysis(session, self.raw_message) + if ( + self.analyze_internal_eml_on_pec + and self.file_mimetype == MimeTypes.EML.value + and self.is_pec() + ): + logger.info("Email is a pec") + report_pec = self._analysis( + session, base64.b64encode(self._real_email).decode("utf-8") + ) + report["pec"] = report_pec + return report + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + return_value=MockUpResponse( + { + "canonical_id": "test", + "subject": "test", + "recipients": ["test"], + "created_at": "test", + "sender": "test", + }, + 200, + ), + ), + patch( + "requests.Session.post", + return_value=MockUpResponse( + { + "message_id": "test", + "raw_message_id": "test", + "flagged_rules": [ + { + "id": "25ae5f27-dee3-4e72-b8b9-3b51d366d695", + "internal_type": None, + "org_id": "b92d89c9-0e04-41ff-9d7b-18a9249bb2ae", + "type": "detection", + "active": True, + "source_md5": "b1b973ccc12158aad82a0c7cb78a4975", + "exclusion_mql": None, + "name": "Attachment: Malicious OneNote Commands", + "authors": [ + { + "name": "Kyle Parrish", + "twitter": "Kyle_Parrish_", + } + ], + "description": "Scans for OneNote attachments", + "references": [], + "tags": ["Suspicious attachment", "Malware"], + "false_positives": None, + "maturity": None, + "severity": "high", + "label": None, + "created_by_api_request_id": None, + "created_by_org_id": None, + "created_by_org_name": None, + "created_by_user_id": None, + "created_by_user_name": None, + "created_at": "2023-03-29 13:25:24.81853+00", + "updated_at": "2023-03-29 13:25:24.81853+00", + "active_updated_at": "2023-03-29T13:27:38.536457Z", + "actions": None, + "immutable": True, + "feed_id": "Test", + "feed_external_rule_id": "test", + } + ], + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/suricata.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/suricata.py new file mode 100644 index 0000000..3cec024 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/suricata.py @@ -0,0 +1,85 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class Suricata(FileAnalyzer, DockerBasedAnalyzer): + name: str = "Suricata" + url: str = "http://pcap_analyzers:4004/suricata" + # interval between http request polling + poll_distance: int = 3 + # http request polling max number of tries + max_tries: int = 100 + # timeout limit + timeout: int = 200 + + reload_rules: bool + extended_logs: bool + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.signatures = {} + + def run(self): + # get binary + binary = self.read_file_bytes() + # make request data + fname = str(self.filename).replace("/", "_").replace(" ", "_") + args = [f"@{fname}", self.md5, str(self.job_id)] + if self.reload_rules: + args.append("--reload_rules") + # the result file is the same one that should be configured in suricata.yml + req_data = { + "args": args, + "timeout": self.timeout, + "callback_context": {"read_result_from": f"/tmp/eve_{self.job_id}"}, + } + req_files = {fname: binary} + + report = self._docker_run(req_data, req_files) + # normalize signatures to facilitate analysis + for detection in report.get("data", []): + alert = detection.get("alert", {}) + signature_name = alert.get("signature") + if signature_name not in self.signatures: + self.signatures[signature_name] = { + "alerts_triggered": 0, + "protocols": [], + "src_ips": [], + "dest_ips": [], + "src_ports": [], + "dest_ports": [], + } + self.signatures[signature_name].update(alert) + # name is already used as dict key + if "signature" in self.signatures[signature_name]: + del self.signatures[signature_name]["signature"] + proto = detection.get("proto", "") + src_ip = detection.get("src_ip", "") + dest_ip = detection.get("dest_ip", "") + src_port = detection.get("src_port", "") + # high irrelevant port numbers + if src_port >= 49152: + src_port = ">49152" + dest_port = detection.get("dest_port", "") + if dest_port >= 49152: + dest_port = ">49152" + self.signatures[signature_name]["alerts_triggered"] += 1 + self._add_item_to_signatures(proto, "protocols", signature_name) + self._add_item_to_signatures(src_ip, "src_ips", signature_name) + self._add_item_to_signatures(dest_ip, "dest_ips", signature_name) + self._add_item_to_signatures(src_port, "src_ports", signature_name) + self._add_item_to_signatures(dest_port, "dest_ports", signature_name) + + report["signatures"] = self.signatures + if not self.extended_logs and "data" in report: + del report["data"] + + return report + + def _add_item_to_signatures(self, item, key, signature_name): + item = str(item) + if item and item not in self.signatures[signature_name][key]: + self.signatures[signature_name][key].append(item) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/thug_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/thug_file.py new file mode 100644 index 0000000..2f86e1f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/thug_file.py @@ -0,0 +1,63 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import secrets + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, FileAnalyzer + + +class ThugFile(FileAnalyzer, DockerBasedAnalyzer): + name: str = "Thug" + url: str = "http://malware_tools_analyzers:4002/thug" + # http request polling max number of tries + max_tries: int = 15 + # interval between http request polling (in secs) + poll_distance: int = 30 + + user_agent: str + dom_events: str + use_proxy: bool + proxy: str + enable_awis: bool + enable_image_processing_analysis: bool + + def _thug_args_builder(self): + user_agent = self.user_agent + dom_events = self.dom_events + use_proxy = self.use_proxy + proxy = self.proxy + enable_awis = self.enable_awis + enable_img_proc = self.enable_image_processing_analysis + # make request arguments + # analysis timeout is set to 5 minutes + args = ["-T", "300", "-u", str(user_agent)] + if dom_events: + args.extend(["-e", str(dom_events)]) + if use_proxy and proxy: + args.extend(["-p", str(proxy)]) + if enable_awis: + args.append("--awis") + if enable_img_proc: + args.append("--image-processing") + + return args + + def run(self): + args = self._thug_args_builder() + # construct a valid dir name into which thug will save the result + fname = str(self.filename).replace("/", "_").replace(" ", "_") + tmp_dir = f"{fname}_{secrets.token_hex(4)}" + # get the file to send + binary = self.read_file_bytes() + # append final arguments, + # -n -> output directory + # -l -> the local file to analyze + args.extend(["-n", "/home/thug/" + tmp_dir, "-l", f"@{fname}"]) + # make request parameters + req_data = { + "args": args, + "callback_context": {"read_result_from": tmp_dir}, + } + req_files = {fname: binary} + + return self._docker_run(req_data, req_files) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/triage_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/triage_scan.py new file mode 100644 index 0000000..8ca80a2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/triage_scan.py @@ -0,0 +1,59 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from api_app.analyzers_manager.observable_analyzers.triage.triage_base import ( + TriageMixin, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class TriageScanFile(FileAnalyzer, TriageMixin): + def run(self): + name_to_send = self.filename if self.filename else self.md5 + binary = self.read_file_bytes() + files = { + "file": (name_to_send, binary), + "_json": (None, b'{"kind": "file", "interactive": false}'), + } + + logger.info(f"triage md5 {self.md5} sending sample for analysis") + for _try in range(self.max_tries): + logger.info(f"triage md5 {self.md5} polling for result try #{_try + 1}") + self.response = self.session.post(self.url + "samples", files=files) + if self.response.status_code == 200: + break + time.sleep(self.poll_distance) + + if self.response: + self.manage_submission_response() + else: + raise AnalyzerRunException(f"response not available for {self.md5}") + + return self.final_report + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + return_value=MockUpResponse( + {"tasks": {"task_1": {}, "task_2": {}}}, 200 + ), + ), + patch( + "requests.Session.post", + return_value=MockUpResponse( + {"id": "sample_id", "status": "pending"}, 200 + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/unpac_me.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/unpac_me.py new file mode 100644 index 0000000..7e7c794 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/unpac_me.py @@ -0,0 +1,127 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class UnpacMe(FileAnalyzer): + url: str = "https://api.unpac.me/api/v1/" + + _api_key_name: str + private: bool + # max no. of tries when polling for result + max_tries: int + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.private: str = "private" if self.private else "public" + + self.headers = {"Authorization": f"Key {self._api_key_name}"} + + # interval b/w HTTP requests when polling + self.poll_distance = 5 + + def run(self): + report = {} + unpac_id = self._upload() + logger.info(f"md5 {self.md5} job {self.job_id} uploaded id {unpac_id}") + for chance in range(self.max_tries): + time.sleep(self.poll_distance) + logger.info( + f"unpacme polling, try n.{chance + 1}." + f" job_id {self.job_id}. starting the query" + ) + status = self._get_status(unpac_id) + logger.info( + f"md5 {self.md5} job {self.job_id} id {unpac_id} status {status}" + ) + if status == "fail": + raise AnalyzerRunException( + f"failed analysis for {self.md5} job {self.job_id}" + ) + if status == "complete": + report = self._get_report(unpac_id) + break + else: + continue + + return report + + def _req_with_checks(self, url, files=None, post=False): + try: + if post: + r = requests.post(self.url + url, files=files, headers=self.headers) + else: + headers = self.headers if self.private == "private" else {} + r = requests.get(self.url + url, files=files, headers=headers) + r.raise_for_status() + except requests.exceptions.HTTPError as e: + logger.error( + f"md5 {self.md5} job {self.job_id} url {url} has http error {str(e)}" + ) + if post: + raise AnalyzerRunException("Monthly quota exceeded!") + raise AnalyzerRunException(e) + except requests.exceptions.Timeout as e: + logger.error( + f"md5 {self.md5} job {self.job_id} url {url} has timeout error {str(e)}" + ) + raise AnalyzerRunException(e) + except requests.exceptions.RequestException as e: + logger.error( + f"md5 {self.md5} job {self.job_id} url {url} failed with error {str(e)}" + ) + raise AnalyzerRunException(e) + return r + + def _upload(self) -> str: + with open(self.filepath, "rb") as f: + file_data = f.read() + files = {"file": (self.filename, file_data)} + r = self._req_with_checks("private/upload", files=files, post=True) + response = r.json() + if "id" not in response: + raise AnalyzerRunException( + f"md5 {self.md5} job {self.job_id} function upload id not in response" + ) + return response["id"] + + def _get_status(self, unpac_me_id) -> str: + response = self._req_with_checks(f"{self.private}/status/{unpac_me_id}") + return response.json().get("status", False) + + def _get_report(self, unpac_me_id) -> Dict: + response = self._req_with_checks(f"{self.private}/results/{unpac_me_id}") + result = response.json() + analysis_id = result.get("id", "") + if analysis_id: + result["permalink"] = f"https://www.unpac.me/results/{analysis_id}" + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + {"id": "test", "status": "complete"}, 200 + ), + ), + patch( + "requests.post", + return_value=MockUpResponse({"id": "test"}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/virushee.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/virushee.py new file mode 100644 index 0000000..a0d1685 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/virushee.py @@ -0,0 +1,97 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time +from typing import Dict, Optional + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class VirusheeFileUpload(FileAnalyzer): + """Analyze a file against Virushee API""" + + max_tries = 30 + poll_distance = 10 + url = "https://api.virushee.com" + + _api_key_name: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.__session = requests.Session() + if not hasattr(self, "_api_key_name"): + logger.info(f"{self.__repr__()} -> Continuing w/o API key..") + else: + self.__session.headers["X-API-Key"] = self._api_key_name + self.force_scan = self._job.tlp == self._job.TLP.CLEAR.value + + def run(self): + binary = self.read_file_bytes() + if not binary: + raise AnalyzerRunException("File is empty") + if not self.force_scan: + hash_result = self.__check_report_for_hash() + if hash_result: + return hash_result + task_id = self.__upload_file(binary) + result = self.__poll_status_and_result(task_id) + return result + + def __check_report_for_hash(self) -> Optional[dict]: + response_json = None + response = self.__session.get(f"{self.url}/file/hash/{self.md5}") + if response.status_code == 404: # hash not found in db + return response_json + response.raise_for_status() + response_json = response.json() + + return response_json + + def __upload_file(self, binary: bytes) -> str: + name_to_send = self.filename if self.filename else self.md5 + files = {"file": (name_to_send, binary)} + response = self.__session.post(f"{self.url}/file/upload", files=files) + response.raise_for_status() + return response.json()["task"] + + def __poll_status_and_result(self, task_id: str) -> dict: + response_json = None + url = f"{self.url}/file/task/{task_id}" + for chance in range(self.max_tries): + logger.info(f"Polling try#{chance+1}") + response = self.__session.get(url) + response.raise_for_status() + response_json = response.json() + if response.status_code == 200: + break + time.sleep(self.poll_distance) + + return response_json + + @classmethod + def _monkeypatch(cls): + cls.poll_distance = 0 # for tests + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + side_effect=[ + MockUpResponse({"message": "hash_not_found"}, 404), + MockUpResponse({"message": "analysis_in_progress"}, 202), + MockUpResponse({"result": "test"}, 200), + ], + ), + patch( + "requests.Session.post", + return_value=MockUpResponse({"task": "123-456-789"}, 201), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/xlm_macro_deobfuscator.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/xlm_macro_deobfuscator.py new file mode 100644 index 0000000..e2425ed --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/xlm_macro_deobfuscator.py @@ -0,0 +1,52 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from celery.exceptions import SoftTimeLimitExceeded +from XLMMacroDeobfuscator.deobfuscator import process_file + +from api_app.analyzers_manager.classes import FileAnalyzer + +logger = logging.getLogger(__name__) + + +class XlmMacroDeobfuscator(FileAnalyzer): + passwords_to_check: list + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + results = {} + try: + for password in self.passwords_to_check: + results = self.decrypt(password) + if results: + break + if not results: + results["error"] = "Can't decrypt with current passwords" + except SoftTimeLimitExceeded: + self._handle_exception("Soft Time Limit Exceeded", is_base_err=True) + return results + + def decrypt(self, xlmpassword=""): + args = { + "file": self.filepath, + "noindent": True, + "noninteractive": True, + "return_deobfuscated": True, + "output_level": 3, + } + if xlmpassword: + args["password"] = xlmpassword + try: + results = {"output": process_file(**args)} + if xlmpassword: + results["password_tested_for_decryption"] = xlmpassword + return results + except Exception as e: + if "Failed to decrypt" in str(e): + return {} + return {"errors": str(e)} diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/yara_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/yara_scan.py new file mode 100644 index 0000000..a94fda0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/yara_scan.py @@ -0,0 +1,440 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import dataclasses +import io +import logging +import os +import zipfile +from pathlib import PosixPath +from typing import Dict, List, Optional, Tuple, Union +from urllib.parse import urlparse + +import git +import requests +import yara +from django.conf import settings +from django.utils.functional import cached_property + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from api_app.models import Parameter, PluginConfig +from intel_owl.settings._util import set_permissions + +logger = logging.getLogger(__name__) + +MAX_YARA_STRINGS = 20 +yara.set_config(max_strings_per_rule=MAX_YARA_STRINGS) + + +@dataclasses.dataclass +class YaraMatchMock: + match: str + strings: List = dataclasses.field(default_factory=list) + tags: List = dataclasses.field(default_factory=list) + meta: Dict = dataclasses.field(default_factory=dict) + + def __str__(self): + return self.match + + +class YaraRepo: + def __init__( + self, + url: str, + owner: str = None, + key: str = None, + directory: PosixPath = None, + ): + self.url = url + self.owner = owner + self.key = key + self._rules: List[yara.Rules] = [] + self._directory = directory + + def __repr__(self): + return f"{self.owner + ': ' if self.owner else ''}{self.url}@{self.directory}" + + @property + def directory(self) -> PosixPath: + if not self._directory: + url_parsed = urlparse(self.url) + if self.is_zip(): + org = url_parsed.netloc + repo = url_parsed.path.split("/")[-1] + else: + path_repo = url_parsed.path.split("/") + # case git@github.com/ORG/repo.git + if len(path_repo) == 2: + org = path_repo[0].split(":")[-1] + repo = path_repo[1] + # case https://github.com/ORG/repo + elif len(path_repo) >= 3: + org = path_repo[1] + repo = path_repo[2] + else: + raise AnalyzerRunException( + f"Unable to update url {self.url}: malformed" + ) + + # we are removing the .zip, .git. .whatever + repo = repo.split(".")[0] + + # directory name is organization_repository + directory_name = "_".join([org, repo]).lower() + path = ( + settings.YARA_RULES_PATH / str(self.owner) + if self.owner + else settings.YARA_RULES_PATH + ) + self._directory = path / directory_name + return self._directory + + def update(self): + logger.info(f"Starting update of {self.url}") + if self.is_zip(): + # private url not supported at the moment for private + self._update_zip() + else: + self._update_git() + + def _update_zip(self): + logger.info(f"About to download zip file from {self.url} to {self.directory}") + response = requests.get(self.url, stream=True) + try: + response.raise_for_status() + except Exception as e: + logger.exception(e) + else: + zipfile_ = zipfile.ZipFile(io.BytesIO(response.content)) + zipfile_.extractall(self.directory) + + def _update_git(self): + try: + if self.key: + ssh_key = self.key.replace("-----BEGIN OPENSSH PRIVATE KEY-----", "") + ssh_key = ssh_key.replace("-----END OPENSSH PRIVATE KEY-----", "") + ssh_key = ssh_key.strip() + ssh_key = ssh_key.replace(" ", "\n") + ssh_key = "-----BEGIN OPENSSH PRIVATE KEY-----\n" + ssh_key + ssh_key = ssh_key + "\n-----END OPENSSH PRIVATE KEY-----\n" + + with open(settings.GIT_KEY_PATH, "w", encoding="utf_8") as f: + f.write(ssh_key) + logger.info( + f"Writing key to download {self.url} " + f"at {str(settings.GIT_KEY_PATH)}" + ) + os.chmod(settings.GIT_KEY_PATH, 0o600) + os.environ["GIT_SSH"] = str(settings.GIT_SSH_SCRIPT_PATH) + logger.info(f"checking {self.directory=} for {self.url=} and {self.owner=}") + + if self.directory.exists(): + # this is to allow a clean pull + for compiled_file in self.compiled_paths: + compiled_file.unlink(missing_ok=True) + + logger.info(f"About to pull {self.url} at {self.directory}") + repo = git.Repo(self.directory) + o = repo.remotes.origin + try: + o.pull(allow_unrelated_histories=True, rebase=True) + except git.exc.GitCommandError as e: + if "index.lock" in e.stderr: + # for some reason the git process did not exit correctly + self.delete_lock_file() + o.pull(allow_unrelated_histories=True, rebase=True) + else: + logger.exception(e) + return + else: + logger.info(f"About to clone {self.url} at {self.directory}") + git.Repo.clone_from(self.url, self.directory, depth=1) + finally: + if self.key: + logger.info("Starting cleanup of git ssh key") + del os.environ["GIT_SSH"] + if settings.GIT_KEY_PATH.exists(): + os.remove(settings.GIT_KEY_PATH) + + def delete_lock_file(self): + lock_file_path = self.directory / ".git" / "index.lock" + lock_file_path.unlink(missing_ok=False) + + @property + def compiled_file_name(self): + return "intel_owl_compiled.yas" + + @cached_property + def first_level_directories(self) -> List[PosixPath]: + paths = [] + if self.directory.exists(): + for directory in self.directory.iterdir(): + if directory.is_dir() and directory.stem not in [".git", ".github"]: + paths.append(directory) + return paths + + @cached_property + def compiled_paths(self) -> List[PosixPath]: + return [ + path / self.compiled_file_name + for path in self.first_level_directories + [self.directory] + ] + + def is_zip(self): + return self.url.endswith(".zip") + + @cached_property + def head_branch(self) -> str: + return git.Repo(self.directory).head.ref.name + + @property + def rules(self) -> List[yara.Rules]: + if not self._rules: + if not self.directory.exists(): + self.update() + for compiled_path in self.compiled_paths: + if compiled_path.exists(): + self._rules.append(yara.load(str(compiled_path))) + else: + self._rules = self.compile() + break + return self._rules + + def rule_url(self, namespace: str) -> Optional[str]: + if self.is_zip(): + return None + namespace = PosixPath(namespace) + if namespace.is_relative_to(self.directory): + relative_part = PosixPath(str(namespace).replace(str(self.directory), "")) + url = self.url[:-4] if self.url.endswith(".git") else self.url + return f"{url}/blob/{self.head_branch}{relative_part}" + else: + logger.error(f"Unable to calculate url from {namespace}") + return None + + def compile(self) -> List[yara.Rules]: + logger.info(f"Starting compile for {self}") + compiled_rules = [] + + for directory in self.first_level_directories + [self.directory]: + if directory != self.directory: + # recursive + rules = directory.rglob("*") + else: + # not recursive + rules = directory.glob("*") + valid_rules_path = [] + for rule in rules: + if rule.stem.endswith("index") or rule.stem.startswith("index"): + continue + if rule.suffix in [".yara", ".yar", ".rule"]: + try: + yara.compile(str(rule)) + except yara.SyntaxError: + continue + else: + valid_rules_path.append(str(rule)) + logger.info( + f"Compiling {len(valid_rules_path)} rules for {self} at {directory}" + ) + compiled_rule = yara.compile( + filepaths={str(path): str(path) for path in valid_rules_path} + ) + compiled_rule.save(str(directory / self.compiled_file_name)) + compiled_rules.append(compiled_rule) + logger.info(f"Rules {self} saved on file") + return compiled_rules + + def analyze(self, file_path: str, filename: str) -> List[Dict]: + logger.info(f"{self} starting analysis of {filename} for file path {file_path}") + result = [] + for rule in self.rules: + rule: yara.Match + try: + matches = rule.match(file_path, externals={"filename": filename}) + except yara.Error as e: + if "internal error" in str(e): + _, code = str(e).split(":") + if int(code.strip()) == 30: + message = f"Too many matches for {filename}" + logger.warning(message) + matches = [YaraMatchMock(message)] + else: + raise e + else: + raise e + + for match in matches: + logger.info( + f"{self} analyzing strings analysis of {filename} for match {match}" + ) + strings = [] + # limited to 20 strings reasons because it could be a very long list + for string in match.strings[:20]: + string: yara.StringMatch + entry = { + "identifier": string.identifier, + "plaintext": [str(i) for i in string.instances[:20]], + } + strings.append(entry) + logger.debug(f"{strings=}") + + logger.info( + f"{self} found {len(strings)} strings for {filename}" + f"for match {match}" + ) + result.append( + { + "match": str(match), + "strings": strings, + "tags": match.tags, + "meta": match.meta, + "path": match.namespace, + "url": self.url, + "rule_url": self.rule_url(match.namespace), + } + ) + return result + + +class YaraStorage: + def __init__(self): + self.repos: List[YaraRepo] = [] + + def add_repo( + self, url: str, owner: str = None, key: str = None, directory: PosixPath = None + ): + new_repo = YaraRepo(url, owner, key, directory) + for i, repo in enumerate(self.repos): + if repo.url == url: + if owner: + if not repo.owner: + self.repos[i] = new_repo + else: + self.repos.append(new_repo) + return + self.repos.append(new_repo) + + def analyze(self, file_path: str, filename: str) -> Dict: + result = {} + errors = [] + for repo in self.repos: + try: + result[str(repo.directory.name)] = repo.analyze(file_path, filename) + # free some memory + repo._rules = [] + except Exception as e: + logger.warning( + f"{filename} rules analysis failed: {e}", stack_info=True + ) + errors.append(str(e)) + return result, errors + + def __repr__(self): + return self.repos.__repr__() + + +class YaraScan(FileAnalyzer): + ignore: list + repositories: list + _private_repositories: dict = {} + local_rules: str + + def _get_owner_and_key(self, url: str) -> Tuple[Union[str, None], Union[str, None]]: + if url in self._private_repositories: + parameter: Parameter = ( + Parameter.objects.filter( + python_module=self.python_module, + is_secret=True, + name="private_repositories", + ) + .annotate_configured(self._config, self._job.user) + .annotate_value_for_user(self._config, self._job.user) + .first() + ) + if ( + parameter + and parameter.configured + and parameter.value + and parameter.is_from_org + ): + if self._job.user.has_membership(): + owner = ( + f"{self._job.user.membership.organization.name}" + f".{self._job.user.membership.organization.owner}" + ) + else: + raise AnalyzerRunException(f"Unable to find repository {url}") + else: + owner = self._job.user.username + key = self._private_repositories[url] + else: + owner = None + key = None + return owner, key + + def run(self): + if not self.repositories: + raise AnalyzerRunException("There are no yara rules selected") + storage = YaraStorage() + for url in self.repositories: + owner, key = self._get_owner_and_key(url) + storage.add_repo(url, owner, key) + if self.local_rules: + path: PosixPath = ( + settings.YARA_RULES_PATH / self._job.user.username / "custom_rule" + ) + if path.exists(): + storage.add_repo( + "", + directory=settings.YARA_RULES_PATH + / self._job.user.username + / "custom_rule", + ) + report, errors = storage.analyze(self.filepath, self.filename) + if errors: + self.report.errors.extend(errors) + self.report.save() + return report + + @classmethod + def _create_storage(cls): + storage = YaraStorage() + + for plugin in PluginConfig.objects.filter( + parameter__name="private_repositories", + parameter__python_module=cls.python_module, + ): + if not plugin.value: + continue + owner = ( + f"{plugin.organization.name}.{plugin.organization.owner}" + if plugin.for_organization + else plugin.owner.username + ) + for url, ssh_key in plugin.value.items(): + logger.info(f"Adding personal private url {url}") + storage.add_repo(url, owner, ssh_key) + + # we are downloading even custom signatures for each analyzer + for plugin in PluginConfig.objects.filter( + parameter__name="repositories", parameter__python_module=cls.python_module + ): + new_urls = plugin.value + logger.info(f"Adding personal urls {new_urls}") + for url in new_urls: + storage.add_repo(url) + return storage + + @classmethod + def update(cls): + logger.info("Starting updating yara rules") + storage = cls._create_storage() + logger.info(f"Urls are {storage}") + for repo in storage.repos: + logger.info(f"Going to update {repo.url} yara repo") + repo.update() + repo.compile() + logger.info("Finished updating yara rules") + set_permissions(settings.YARA_RULES_PATH) + return True diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/yaraify_file_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/yaraify_file_scan.py new file mode 100644 index 0000000..a1906c4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/yaraify_file_scan.py @@ -0,0 +1,134 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +import logging +import time +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from api_app.analyzers_manager.observable_analyzers.yaraify import YARAify +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class YARAifyFileScan(FileAnalyzer, YARAify): + _api_key_identifier: str + clamav_scan: bool + unpack: bool + share_file: bool + skip_noisy: bool + skip_known: bool + + def config(self, runtime_configuration: Dict): + FileAnalyzer.config(self, runtime_configuration) + self.query = "lookup_hash" + YARAify.config(self, runtime_configuration) + self.search_term = self.md5 + + self.max_tries = 200 + self.poll_distance = 3 + + self.send_file = self._job.tlp == self._job.TLP.CLEAR.value + if self.send_file and not hasattr(self, "_api_key_identifier"): + raise AnalyzerConfigurationException( + "Unable to send file without having api_key_identifier set" + ) + + def run(self): + name_to_send = self.filename if self.filename else self.md5 + file = self.read_file_bytes() + logger.info(f"checking hash: {self.md5}") + + hash_scan = YARAify.run(self) + query_status = hash_scan.get("query_status") + logger.info(f"{query_status=} for hash {self.md5}") + + if query_status == "ok": + logger.info(f"found YARAify hash scan and returning it. {self.md5}") + return hash_scan + + result = hash_scan + if self.send_file: + data = { + "identifier": self._api_key_identifier, + # the server wants either 0 or 1 + # https://yaraify.abuse.ch/api/#file-scan + "clamav_scan": int(self.clamav_scan), + "unpack": int(self.unpack), + "share_file": int(self.share_file), + "skip_noisy": int(self.skip_noisy), + "skip_known": int(self.skip_known), + } + + files_ = { + "json_data": (None, json.dumps(data), "application/json"), + "file": (name_to_send, file), + } + logger.info(f"yara file scan md5 {self.md5} sending sample for analysis") + response = requests.post(self.url, files=files_) + response.raise_for_status() + scan_response = response.json() + scan_query_status = scan_response.get("query_status") + if scan_query_status == "queued": + task_id = scan_response.get("data", {}).get("task_id", "") + if not task_id: + raise AnalyzerRunException( + f"task_id value is unexpected: {task_id}." + f"Analysis was requested for md5 {self.md5}" + ) + for _try in range(self.max_tries): + try: + logger.info( + f"yara file scan md5 {self.md5} polling for" + f" result try #{_try + 1}." + f"task_id: {task_id}" + ) + data = {"query": "get_results", "task_id": task_id} + response = requests.post(self.url, json=data) + response.raise_for_status() + task_response = response.json() + logger.debug(task_response) + data_results = task_response.get("data") + if isinstance(data_results, dict) and data_results: + logger.info(f"found scan result for {self.md5}") + break + except requests.RequestException as e: + logger.warning(e, stack_info=True) + time.sleep(self.poll_distance) + else: + raise AnalyzerRunException( + f"query_status value is unexpected: {scan_query_status}." + f"Analysis was requested for md5 {self.md5}" + ) + + result = response.json() + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + side_effect=[ + MockUpResponse({"query_status": "not-available"}, 200), + MockUpResponse( + {"query_status": "queued", "data": {"task_id": 123}}, 200 + ), + MockUpResponse( + {"query_status": "ok", "data": {"static_results": []}}, 200 + ), + ], + ) + ) + ] + return FileAnalyzer._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/zippy_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/zippy_scan.py new file mode 100644 index 0000000..6da9fc4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/file_analyzers/zippy_scan.py @@ -0,0 +1,51 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging + +from zippy import CompressionEngine, EnsembledZippy, Zippy + +from api_app.analyzers_manager.classes import FileAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + +logger = logging.getLogger(__name__) + + +class ZippyAnalyser(FileAnalyzer): + """ + Tells if a file is written by HUMAN or AI + """ + + ENGINES = { + "lzma": CompressionEngine.LZMA, + "zlib": CompressionEngine.ZLIB, + "brotli": CompressionEngine.BROTLI, + } + + engine: str + + def update(self): + pass + + def run(self): + z = ( + Zippy(engine=self.ENGINES[self.engine]) + if self.engine in self.ENGINES + else EnsembledZippy() + ) + + logger.info(f"Running Zippy on file {self.filepath} using {self.engine}") + binary_data = self.read_file_bytes() + try: + text_data = binary_data.decode("utf-8") + response = z.run_on_file_chunked(filename=self.filepath) + except UnicodeDecodeError: + logger.exception(f"Cannot decode file {self.filepath}") + raise AnalyzerRunException("Cannot decode file") + except Exception as e: + logger.exception( + f"%{self.engine}.run_on_text_chunked(text_data) failed: {e}" + ) + raise AnalyzerRunException(f"{self.engine} failed") + response = response + (text_data, "engine used: " + self.engine) + # returning a response tuple with the text checked and AI or HUMAN + return response diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/filters.py b/Submodules/IntelOwl/api_app/analyzers_manager/filters.py new file mode 100644 index 0000000..e3b5269 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/filters.py @@ -0,0 +1,15 @@ +import rest_framework_filters as filters + +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.fields import ChoiceArrayField + + +class AnalyzerConfigFilter(filters.FilterSet): + class Meta: + model = AnalyzerConfig + filter_overrides = { + ChoiceArrayField: { + "filter_class": filters.ChoiceFilter, + } + } + fields = {"type": ["exact"], "observable_supported": ["in"]} diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0001_initial_squashed.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0001_initial_squashed.py new file mode 100644 index 0000000..809a0f1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0001_initial_squashed.py @@ -0,0 +1,376 @@ +# Generated by Django 4.2.8 on 2024-02-08 10:17 + +import django.contrib.postgres.fields +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + +import api_app.fields +import api_app.validators + + +class Migration(migrations.Migration): + initial = True + + dependencies = [("api_app", "0001_1_initial_squashed")] + + operations = [ + migrations.CreateModel( + name="AnalyzerConfig", + fields=[ + ( + "name", + models.CharField( + max_length=100, + primary_key=True, + serialize=False, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", + "Your name should match the [A-Za-z0-9_] characters", + ) + ], + ), + ), + ( + "type", + models.CharField( + choices=[("file", "File"), ("observable", "Observable")], + max_length=50, + ), + ), + ( + "python_module", + models.ForeignKey( + limit_choices_to={ + "base_path__in": [ + "api_app.analyzers_manager.file_analyzers", + "api_app.analyzers_manager.observable_analyzers", + ] + }, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)ss", + to="api_app.pythonmodule", + ), + ), + ("description", models.TextField()), + ("disabled", models.BooleanField(default=False)), + ( + "disabled_in_organizations", + models.ManyToManyField( + blank=True, + related_name="%(app_label)s_%(class)s_disabled", + to="certego_saas_organization.organization", + ), + ), + ("docker_based", models.BooleanField(default=False)), + ( + "observable_supported", + api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("ip", "Ip"), + ("url", "Url"), + ("domain", "Domain"), + ("hash", "Hash"), + ("generic", "Generic"), + ], + max_length=30, + ), + blank=True, + default=list, + size=None, + ), + ), + ( + "supported_filetypes", + api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("application/w-script-file", "Wscript"), + ("application/javascript", "Javascript1"), + ("application/x-javascript", "Javascript2"), + ("text/javascript", "Javascript3"), + ("application/x-vbscript", "Vb Script"), + ("text/x-ms-iqy", "Iqy"), + ("application/vnd.android.package-archive", "Apk"), + ("application/x-dex", "Dex"), + ("application/onenote", "One Note"), + ("application/zip", "Zip1"), + ("multipart/x-zip", "Zip2"), + ("application/java-archive", "Java"), + ("text/rtf", "Rtf1"), + ("application/rtf", "Rtf2"), + ("application/x-dosexec", "Dos"), + ("application/x-sharedlib", "Shared Lib"), + ("application/x-executable", "Exe"), + ("application/x-elf", "Elf"), + ("application/octet-stream", "Octet"), + ("application/vnd.tcpdump.pcap", "Pcap"), + ("application/pdf", "Pdf"), + ("text/html", "Html"), + ("application/x-mspublisher", "Pub"), + ( + "application/vnd.ms-excel.addin.macroEnabled", + "Excel Macro1", + ), + ( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Excel Macro2", + ), + ("application/vnd.ms-excel", "Excel1"), + ("application/excel", "Excel2"), + ( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Doc", + ), + ("application/xml", "Xml1"), + ("text/xml", "Xml2"), + ("application/encrypted", "Encrypted"), + ("text/plain", "Plain"), + ("text/csv", "Csv"), + ( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Pptx", + ), + ("application/msword", "Word1"), + ( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Word2", + ), + ("application/vnd.ms-powerpoint", "Powerpoint"), + ("application/vnd.ms-office", "Office"), + ("application/x-binary", "Binary"), + ("application/x-macbinary", "Mac1"), + ("application/mac-binary", "Mac2"), + ("application/x-mach-binary", "Mac3"), + ("application/x-zip-compressed", "Compress1"), + ("application/x-compressed", "Compress2"), + ("application/vnd.ms-outlook", "Outlook"), + ("message/rfc822", "Eml"), + ("application/pkcs7-signature", "Pkcs7"), + ("application/x-pkcs7-signature", "Xpkcs7"), + ("multipart/mixed", "Mixed"), + ("text/x-shellscript", "X Shellscript"), + ], + max_length=90, + ), + blank=True, + default=list, + size=None, + ), + ), + ( + "not_supported_filetypes", + api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("application/w-script-file", "Wscript"), + ("application/javascript", "Javascript1"), + ("application/x-javascript", "Javascript2"), + ("text/javascript", "Javascript3"), + ("application/x-vbscript", "Vb Script"), + ("text/x-ms-iqy", "Iqy"), + ("application/vnd.android.package-archive", "Apk"), + ("application/x-dex", "Dex"), + ("application/onenote", "One Note"), + ("application/zip", "Zip1"), + ("multipart/x-zip", "Zip2"), + ("application/java-archive", "Java"), + ("text/rtf", "Rtf1"), + ("application/rtf", "Rtf2"), + ("application/x-dosexec", "Dos"), + ("application/x-sharedlib", "Shared Lib"), + ("application/x-executable", "Exe"), + ("application/x-elf", "Elf"), + ("application/octet-stream", "Octet"), + ("application/vnd.tcpdump.pcap", "Pcap"), + ("application/pdf", "Pdf"), + ("text/html", "Html"), + ("application/x-mspublisher", "Pub"), + ( + "application/vnd.ms-excel.addin.macroEnabled", + "Excel Macro1", + ), + ( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Excel Macro2", + ), + ("application/vnd.ms-excel", "Excel1"), + ("application/excel", "Excel2"), + ( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Doc", + ), + ("application/xml", "Xml1"), + ("text/xml", "Xml2"), + ("application/encrypted", "Encrypted"), + ("text/plain", "Plain"), + ("text/csv", "Csv"), + ( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Pptx", + ), + ("application/msword", "Word1"), + ( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Word2", + ), + ("application/vnd.ms-powerpoint", "Powerpoint"), + ("application/vnd.ms-office", "Office"), + ("application/x-binary", "Binary"), + ("application/x-macbinary", "Mac1"), + ("application/mac-binary", "Mac2"), + ("application/x-mach-binary", "Mac3"), + ("application/x-zip-compressed", "Compress1"), + ("application/x-compressed", "Compress2"), + ("application/vnd.ms-outlook", "Outlook"), + ("message/rfc822", "Eml"), + ("application/pkcs7-signature", "Pkcs7"), + ("application/x-pkcs7-signature", "Xpkcs7"), + ("multipart/mixed", "Mixed"), + ("text/x-shellscript", "X Shellscript"), + ], + max_length=90, + ), + blank=True, + default=list, + size=None, + ), + ), + ("run_hash", models.BooleanField(default=False)), + ( + "run_hash_type", + models.CharField( + blank=True, + choices=[("md5", "Md5"), ("sha256", "Sha256")], + max_length=10, + ), + ), + ( + "health_check_status", + models.BooleanField(default=True, editable=False), + ), + ( + "health_check_task", + models.OneToOneField( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="healthcheck_for_%(class)s", + to="django_celery_beat.periodictask", + ), + ), + ( + "soft_time_limit", + models.IntegerField( + default=60, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ("routing_key", models.CharField(default="default", max_length=50)), + ( + "maximum_tlp", + models.CharField( + choices=[ + ("CLEAR", "Clear"), + ("GREEN", "Green"), + ("AMBER", "Amber"), + ("RED", "Red"), + ], + default="RED", + max_length=50, + ), + ), + ], + options={"abstract": False, "ordering": ["name", "disabled"]}, + ), + migrations.AddIndex( + model_name="analyzerconfig", + index=models.Index( + fields=["python_module", "disabled"], + name="analyzers_m_python__3e6166_idx", + ), + ), + migrations.RenameIndex( + model_name="analyzerconfig", + new_name="analyzers_m_python__657cda_idx", + old_name="analyzers_m_python__3e6166_idx", + ), + migrations.CreateModel( + name="AnalyzerReport", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("FAILED", "Failed"), + ("PENDING", "Pending"), + ("RUNNING", "Running"), + ("SUCCESS", "Success"), + ("KILLED", "Killed"), + ], + max_length=50, + ), + ), + ("report", models.JSONField(default=dict)), + ( + "errors", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=512), + blank=True, + default=list, + size=None, + ), + ), + ("start_time", models.DateTimeField(default=django.utils.timezone.now)), + ("end_time", models.DateTimeField(default=django.utils.timezone.now)), + ("task_id", models.UUIDField()), + ( + "job", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_app.job", + ), + ), + ("sent_to_bi", models.BooleanField(default=False, editable=False)), + ("parameters", models.JSONField(default={}, editable=False)), + ( + "config", + models.ForeignKey( + "AnalyzerConfig", + related_name="reports", + on_delete=models.CASCADE, + ), + ), + ], + ), + migrations.AlterUniqueTogether( + name="analyzerreport", + unique_together={("config", "job")}, + ), + migrations.AddIndex( + model_name="analyzerreport", + index=models.Index( + fields=["sent_to_bi", "-start_time"], name="analyzerreportsBISearch" + ), + ), + migrations.AlterField( + model_name="analyzerreport", + name="parameters", + field=models.JSONField(editable=False), + ), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0000_analyzer_config_apkid.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0000_analyzer_config_apkid.py new file mode 100644 index 0000000..9b36d73 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0000_analyzer_config_apkid.py @@ -0,0 +1,109 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "APKiD", + "python_module": { + "module": "apkid.APKiD", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "[APKiD](https://github.com/rednaga/APKiD) identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.", + "disabled": False, + "soft_time_limit": 400, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/zip", + "application/java-archive", + "application/vnd.android.package-archive", + "application/x-dex", + "application/vnd.android.package-archive", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0001_initial_squashed"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0001_analyzer_config_abuseipdb.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0001_analyzer_config_abuseipdb.py new file mode 100644 index 0000000..516fe1e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0001_analyzer_config_abuseipdb.py @@ -0,0 +1,215 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "AbuseIPDB", + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if an ip was reported on [AbuseIPDB](https://www.abuseipdb.com/)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_age", + "type": "int", + "description": "How many days back you want to check for reports. Default 180", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "Reports are included in this response if you enable this flag. Omitting the verbose flag will exclude reports and the country name field. If you want to keep your response payloads light, this is recommended", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_reports", + "type": "int", + "description": "How many reports you want to save. Default 200", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_age", + "type": "int", + "description": "How many days back you want to check for reports. Default 180", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 180, + "updated_at": "2024-02-09T10:52:16.520330Z", + "owner": None, + "analyzer_config": "AbuseIPDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "Reports are included in this response if you enable this flag. Omitting the verbose flag will exclude reports and the country name field. If you want to keep your response payloads light, this is recommended", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.533068Z", + "owner": None, + "analyzer_config": "AbuseIPDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "abuseipdb.AbuseIPDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_reports", + "type": "int", + "description": "How many reports you want to save. Default 200", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 200, + "updated_at": "2024-02-09T10:52:16.544831Z", + "owner": None, + "analyzer_config": "AbuseIPDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0000_analyzer_config_apkid"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0002_analyzer_config_anomali_threatstream.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0002_analyzer_config_anomali_threatstream.py new file mode 100644 index 0000000..675f02d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0002_analyzer_config_anomali_threatstream.py @@ -0,0 +1,292 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Anomali_Threatstream", + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Analyzer to interact with Anomali ThreatStream APIs", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "minimal_confidence", + "type": "str", + "description": "Minimal Confidence filter", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "str", + "description": "Number of maximal entries returned", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "threatstream_analysis", + "type": "str", + "description": "API endpoint called: options are `confidence`, `intelligence` and `passive_dns`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "modified_after", + "type": "str", + "description": "Filter on entries modified after a specific date. Date must be specified in this format: YYYYMMDDThhmmss where T denotes the start of the value for time, in UTC time. For example, 2014-10-02T20:44:35.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "must_active", + "type": "bool", + "description": "Only return active entries", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_user_name", + "type": "str", + "description": "API USER for Anomali Threatstream", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API Key for Anomali Threatstream", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "minimal_confidence", + "type": "str", + "description": "Minimal Confidence filter", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "0", + "updated_at": "2024-02-09T10:52:21.892899Z", + "owner": None, + "analyzer_config": "Anomali_Threatstream", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "str", + "description": "Number of maximal entries returned", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "100", + "updated_at": "2024-02-09T10:52:21.812344Z", + "owner": None, + "analyzer_config": "Anomali_Threatstream", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "threatstream_analysis", + "type": "str", + "description": "API endpoint called: options are `confidence`, `intelligence` and `passive_dns`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "intelligence", + "updated_at": "2024-02-09T10:52:21.855528Z", + "owner": None, + "analyzer_config": "Anomali_Threatstream", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "modified_after", + "type": "str", + "description": "Filter on entries modified after a specific date. Date must be specified in this format: YYYYMMDDThhmmss where T denotes the start of the value for time, in UTC time. For example, 2014-10-02T20:44:35.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "1900-10-02T20:44:35", + "updated_at": "2024-02-09T10:52:21.947784Z", + "owner": None, + "analyzer_config": "Anomali_Threatstream", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "threatstream.Threatstream", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "must_active", + "type": "bool", + "description": "Only return active entries", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:22.000752Z", + "owner": None, + "analyzer_config": "Anomali_Threatstream", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0001_analyzer_config_abuseipdb"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0003_analyzer_config_auth0.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0003_analyzer_config_auth0.py new file mode 100644 index 0000000..78b0458 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0003_analyzer_config_auth0.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Auth0", + "python_module": { + "module": "auth0.Auth0", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP against the Auth0 API", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "auth0.Auth0", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0002_analyzer_config_anomali_threatstream"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0004_analyzer_config_binaryedge.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0004_analyzer_config_binaryedge.py new file mode 100644 index 0000000..69c8970 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0004_analyzer_config_binaryedge.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "BinaryEdge", + "python_module": { + "module": "binaryedge.BinaryEdge", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Details about an Host. List of recent events for the specified host, including details of exposed ports and services and return list of subdomains known from the target domains", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "binaryedge.BinaryEdge", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for the BinaryEdge", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0003_analyzer_config_auth0"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0005_analyzer_config_bitcoinabuse.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0005_analyzer_config_bitcoinabuse.py new file mode 100644 index 0000000..7f16b53 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0005_analyzer_config_bitcoinabuse.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "BitcoinAbuse", + "python_module": { + "module": "bitcoinabuse.BitcoinAbuseAPI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check a BTC address against bitcoinabuse.com, a public database of BTC addresses used by hackers and criminals.", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "bitcoinabuse.BitcoinAbuseAPI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0004_analyzer_config_binaryedge"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0006_analyzer_config_boxjs.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0006_analyzer_config_boxjs.py new file mode 100644 index 0000000..4316a50 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0006_analyzer_config_boxjs.py @@ -0,0 +1,107 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "BoxJS", + "python_module": { + "module": "boxjs_scan.BoxJS", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "A tool for studying JavaScript malware", + "disabled": False, + "soft_time_limit": 400, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/x-javascript", + "application/javascript", + "text/javascript", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0005_analyzer_config_bitcoinabuse"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0007_analyzer_config_circlpassivedns.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0007_analyzer_config_circlpassivedns.py new file mode 100644 index 0000000..87120eb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0007_analyzer_config_circlpassivedns.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CIRCLPassiveDNS", + "python_module": { + "module": "circl_pdns.CIRCL_PDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable against the CIRCL Passive DNS DB", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "circl_pdns.CIRCL_PDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "pdns_credentials", + "type": "str", + "description": "Template to use: `|`.", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0006_analyzer_config_boxjs"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0008_analyzer_config_circlpassivessl.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0008_analyzer_config_circlpassivessl.py new file mode 100644 index 0000000..c048fd1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0008_analyzer_config_circlpassivessl.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CIRCLPassiveSSL", + "python_module": { + "module": "circl_pssl.CIRCL_PSSL", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable against the CIRCL Passive SSL DB", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "circl_pssl.CIRCL_PSSL", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "pdns_credentials", + "type": "str", + "description": "Template to use: `|`.", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0007_analyzer_config_circlpassivedns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0009_analyzer_config_crxcavator.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0009_analyzer_config_crxcavator.py new file mode 100644 index 0000000..2db640b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0009_analyzer_config_crxcavator.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CRXcavator", + "python_module": { + "module": "crxcavator.CRXcavator", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scans a chrome extension against crxcavator.io service. Every Chrome-Extension has a unique alpha=numeric identifier. That's the only Input necessary. Eg: `Norton Safe Search Enhanced`'s identifier is `eoigllimhcllmhedfbmahegmoakcdakd`.", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0008_analyzer_config_circlpassivessl"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0010_analyzer_config_capa_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0010_analyzer_config_capa_info.py new file mode 100644 index 0000000..f5f5cf3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0010_analyzer_config_capa_info.py @@ -0,0 +1,176 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Capa_Info", + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "capa detects capabilities in executable files", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/x-dosexec", + "application/x-sharedlib", + "application/x-executable", + "application/x-elf", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "`32` or `64`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "if the file analyzed is a shellcode or not", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "`32` or `64`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "64", + "updated_at": "2024-02-09T10:52:16.657638Z", + "owner": None, + "analyzer_config": "Capa_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "if the file analyzed is a shellcode or not", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:16.669451Z", + "owner": None, + "analyzer_config": "Capa_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0009_analyzer_config_crxcavator"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0011_analyzer_config_capa_info_shellcode.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0011_analyzer_config_capa_info_shellcode.py new file mode 100644 index 0000000..ad1ddd4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0011_analyzer_config_capa_info_shellcode.py @@ -0,0 +1,171 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Capa_Info_Shellcode", + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "capa detects capabilities in shellcode files", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/octet-stream"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "`32` or `64`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "if the file analyzed is a shellcode or not", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "`32` or `64`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "64", + "updated_at": "2024-02-09T10:52:16.680543Z", + "owner": None, + "analyzer_config": "Capa_Info_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "capa_info.CapaInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "if the file analyzed is a shellcode or not", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.694289Z", + "owner": None, + "analyzer_config": "Capa_Info_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0010_analyzer_config_capa_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0012_analyzer_config_capesandbox.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0012_analyzer_config_capesandbox.py new file mode 100644 index 0000000..3a8216a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0012_analyzer_config_capesandbox.py @@ -0,0 +1,600 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CapeSandbox", + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Automatic scan of suspicious files using [CapeSandbox](https://github.com/kevoreilly/CAPEv2) API", + "disabled": False, + "soft_time_limit": 1000, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "Number of max tries while trying to poll the CAPESandbox API.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "Seconds to wait before moving on to the next poll attempt.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "URL for the CapeSandbox instance. If none provided, It uses the API provided by CAPESandbox by default.", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "options", + "type": "str", + "description": 'Specify options for the analysis package (e.g. "name=value,name2=value2").', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "package", + "type": "str", + "description": "Specify an analysis package.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Specify an analysis timeout.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "priority", + "type": "int", + "description": "Specify a priority for the analysis (1=low, 2=medium, 3=high).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "machine", + "type": "str", + "description": "Specify the identifier of a machine you want to use (empty = first available).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "platform", + "type": "str", + "description": "Specify the operating system platform you want to use (windows/darwin/linux).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "memory", + "type": "bool", + "description": "Enable to take a memory dump of the analysis machine.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "enforce_timeout", + "type": "bool", + "description": "Enable to force the analysis to run for the full timeout period.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "custom", + "type": "str", + "description": "Specify any custom value.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "tags", + "type": "str", + "description": "Specify tags identifier of a machine you want to use.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "route", + "type": "str", + "description": "Specify an analysis route.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "certificate", + "type": "str", + "description": "CapSandbox SSL certificate (multiline string).", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "requests_timeout", + "type": "int", + "description": "Python requests HTTP GET/POST timeout", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "Number of max tries while trying to poll the CAPESandbox API.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 50, + "updated_at": "2024-02-09T10:52:22.114079Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "Seconds to wait before moving on to the next poll attempt.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:22.141845Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "options", + "type": "str", + "description": 'Specify options for the analysis package (e.g. "name=value,name2=value2").', + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.260381Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "package", + "type": "str", + "description": "Specify an analysis package.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.321897Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Specify an analysis timeout.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 180, + "updated_at": "2024-02-09T10:52:22.367839Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "priority", + "type": "int", + "description": "Specify a priority for the analysis (1=low, 2=medium, 3=high).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:22.397102Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "machine", + "type": "str", + "description": "Specify the identifier of a machine you want to use (empty = first available).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.423279Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "platform", + "type": "str", + "description": "Specify the operating system platform you want to use (windows/darwin/linux).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.469120Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "memory", + "type": "bool", + "description": "Enable to take a memory dump of the analysis machine.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:22.495485Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "enforce_timeout", + "type": "bool", + "description": "Enable to force the analysis to run for the full timeout period.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:22.519750Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "custom", + "type": "str", + "description": "Specify any custom value.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.549002Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "tags", + "type": "str", + "description": "Specify tags identifier of a machine you want to use.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.575749Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "route", + "type": "str", + "description": "Specify an analysis route.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:22.599727Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cape_sandbox.CAPEsandbox", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "requests_timeout", + "type": "int", + "description": "Python requests HTTP GET/POST timeout", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:28.988123Z", + "owner": None, + "analyzer_config": "CapeSandbox", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0011_analyzer_config_capa_info_shellcode"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0013_analyzer_config_censys_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0013_analyzer_config_censys_search.py new file mode 100644 index 0000000..dea36a9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0013_analyzer_config_censys_search.py @@ -0,0 +1,160 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Censys_Search", + "python_module": { + "module": "censys.Censys", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP address against Censys View API", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "censys.Censys", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "censys_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "censys.Censys", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_id_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "censys.Censys", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_secret_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "censys.Censys", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "censys_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "search", + "updated_at": "2024-02-09T10:52:16.706940Z", + "owner": None, + "analyzer_config": "Censys_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0012_analyzer_config_capesandbox"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0014_analyzer_config_checkdmarc.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0014_analyzer_config_checkdmarc.py new file mode 100644 index 0000000..3f9ad35 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0014_analyzer_config_checkdmarc.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CheckDMARC", + "python_module": { + "module": "checkdmarc.CheckDMARC", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "An SPF and DMARC DNS records validator", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0013_analyzer_config_censys_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0015_analyzer_config_checkphish.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0015_analyzer_config_checkphish.py new file mode 100644 index 0000000..7c62da5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0015_analyzer_config_checkphish.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CheckPhish", + "python_module": { + "module": "checkphish.CheckPhish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "[CheckPhish](https://checkphish.ai/checkphish-api/) can detect phishing and fraudulent sites", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "long", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "GREEN", + "observable_supported": ["url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "checkphish.CheckPhish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "polling_time", + "type": "float", + "description": "IntelOwl would sleep for this time between each poll to CheckPhish APIs", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "checkphish.CheckPhish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "polling_tries", + "type": "int", + "description": "How many times we poll the CheckPhish API for scan results", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "checkphish.CheckPhish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API Key for the analyzer", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "checkphish.CheckPhish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "polling_time", + "type": "float", + "description": "IntelOwl would sleep for this time between each poll to CheckPhish APIs", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 0.5, + "updated_at": "2024-02-09T10:52:16.582169Z", + "owner": None, + "analyzer_config": "CheckPhish", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "checkphish.CheckPhish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "polling_tries", + "type": "int", + "description": "How many times we poll the CheckPhish API for scan results", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:16.595613Z", + "owner": None, + "analyzer_config": "CheckPhish", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0014_analyzer_config_checkdmarc"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0016_analyzer_config_clamav.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0016_analyzer_config_clamav.py new file mode 100644 index 0000000..c246154 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0016_analyzer_config_clamav.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "ClamAV", + "python_module": { + "module": "clamav.ClamAV", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "scan files using [ClamAV AntiVirus Engine](https://www.clamav.net/). IntelOwl automatically keep ClamAV updated with official and [unofficial](https://github.com/rseichter/fangfrisch) open source signatures", + "disabled": False, + "soft_time_limit": 70, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0015_analyzer_config_checkphish"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0017_analyzer_config_classic_dns.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0017_analyzer_config_classic_dns.py new file mode 100644 index 0000000..32195ce --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0017_analyzer_config_classic_dns.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Classic_DNS", + "python_module": { + "module": "dns.dns_resolvers.classic_dns_resolver.ClassicDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Retrieve current domain resolution with default DNS", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_resolvers.classic_dns_resolver.ClassicDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns.dns_resolvers.classic_dns_resolver.ClassicDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "A", + "updated_at": "2024-02-09T10:52:16.626543Z", + "owner": None, + "analyzer_config": "Classic_DNS", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0016_analyzer_config_clamav"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0018_analyzer_config_cloudflare_dns.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0018_analyzer_config_cloudflare_dns.py new file mode 100644 index 0000000..d4a52bd --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0018_analyzer_config_cloudflare_dns.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CloudFlare_DNS", + "python_module": { + "module": "dns.dns_resolvers.cloudflare_dns_resolver.CloudFlareDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Retrieve current domain resolution with CloudFlare DoH (DNS over HTTPS)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_resolvers.cloudflare_dns_resolver.CloudFlareDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns.dns_resolvers.cloudflare_dns_resolver.CloudFlareDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "A", + "updated_at": "2024-02-09T10:52:16.638321Z", + "owner": None, + "analyzer_config": "CloudFlare_DNS", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0017_analyzer_config_classic_dns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0019_analyzer_config_cloudflare_malicious_detector.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0019_analyzer_config_cloudflare_malicious_detector.py new file mode 100644 index 0000000..4a1efad --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0019_analyzer_config_cloudflare_malicious_detector.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CloudFlare_Malicious_Detector", + "python_module": { + "module": "dns.dns_malicious_detectors.cloudflare_malicious_detector.CloudFlareMaliciousDetector", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scan an observable against CloudFlare DB", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0018_analyzer_config_cloudflare_dns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0020_analyzer_config_crowdsec.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0020_analyzer_config_crowdsec.py new file mode 100644 index 0000000..cb9a545 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0020_analyzer_config_crowdsec.py @@ -0,0 +1,118 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Crowdsec", + "python_module": { + "module": "crowdsec.Crowdsec", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if an IP was reported on [Crowdsec](https://www.crowdsec.net/) Smoke Dataset", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "crowdsec.Crowdsec", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API Key to access the service", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "analyzers_manager", + "0002_0019_analyzer_config_cloudflare_malicious_detector", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0021_analyzer_config_cuckoo_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0021_analyzer_config_cuckoo_scan.py new file mode 100644 index 0000000..926373a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0021_analyzer_config_cuckoo_scan.py @@ -0,0 +1,193 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Cuckoo_Scan", + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "scan a file on a Cuckoo instance", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_poll_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_post_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_poll_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 20, + "updated_at": "2024-02-09T10:52:16.730910Z", + "owner": None, + "analyzer_config": "Cuckoo_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cuckoo_scan.CuckooAnalysis", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_post_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 5, + "updated_at": "2024-02-09T10:52:16.742475Z", + "owner": None, + "analyzer_config": "Cuckoo_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0020_analyzer_config_crowdsec"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0022_analyzer_config_cyberchef.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0022_analyzer_config_cyberchef.py new file mode 100644 index 0000000..b1a87ed --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0022_analyzer_config_cyberchef.py @@ -0,0 +1,204 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "CyberChef", + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Bake an input with a selected CyberChef Recipe", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "local", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "output_type", + "type": "str", + "description": "Output type of the recipe. Leave blank to use default. Available output types are listed [here](https://github.com/gchq/CyberChef/wiki/Adding-a-new-operation#data-types)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "recipe_code", + "type": "list", + "description": "Custom recipe to use (instead of a predefined one). [Here](https://github.com/mattnotmax/cyberchef-recipes) are some sample recipes. NOTE: This is only read if recipe_name is blank", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "recipe_name", + "type": "str", + "description": "Name of pre-defined recipe to use.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "output_type", + "type": "str", + "description": "Output type of the recipe. Leave blank to use default. Available output types are listed [here](https://github.com/gchq/CyberChef/wiki/Adding-a-new-operation#data-types)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.777987Z", + "owner": None, + "analyzer_config": "CyberChef", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "recipe_code", + "type": "list", + "description": "Custom recipe to use (instead of a predefined one). [Here](https://github.com/mattnotmax/cyberchef-recipes) are some sample recipes. NOTE: This is only read if recipe_name is blank", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [], + "updated_at": "2024-02-09T10:52:16.789453Z", + "owner": None, + "analyzer_config": "CyberChef", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "cyberchef.CyberChef", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "recipe_name", + "type": "str", + "description": "Name of pre-defined recipe to use.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "to decimal", + "updated_at": "2024-02-09T10:52:16.803010Z", + "owner": None, + "analyzer_config": "CyberChef", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0021_analyzer_config_cuckoo_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0023_analyzer_config_cymru_hash_registry_get_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0023_analyzer_config_cymru_hash_registry_get_file.py new file mode 100644 index 0000000..b5b7e6a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0023_analyzer_config_cymru_hash_registry_get_file.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Cymru_Hash_Registry_Get_File", + "python_module": { + "module": "cymru.Cymru", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a particular file is known to be malware by Team Cymru", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0022_analyzer_config_cyberchef"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0024_analyzer_config_cymru_hash_registry_get_observable.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0024_analyzer_config_cymru_hash_registry_get_observable.py new file mode 100644 index 0000000..b2c11fe --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0024_analyzer_config_cymru_hash_registry_get_observable.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Cymru_Hash_Registry_Get_Observable", + "python_module": { + "module": "cymru.Cymru", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a particular hash is available in the malware hash registry of Team Cymru", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0023_analyzer_config_cymru_hash_registry_get_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0025_analyzer_config_dns0_eu.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0025_analyzer_config_dns0_eu.py new file mode 100644 index 0000000..ada1b7b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0025_analyzer_config_dns0_eu.py @@ -0,0 +1,141 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNS0_EU", + "python_module": { + "module": "dns.dns_resolvers.dns0_eu_resolver.DNS0EUResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Retrieve current domain resolution with DNS0.eu DoH (DNS over HTTPS)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_resolvers.dns0_eu_resolver.DNS0EUResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns.dns_resolvers.dns0_eu_resolver.DNS0EUResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "A", + "updated_at": "2024-02-09T10:52:16.904103Z", + "owner": None, + "analyzer_config": "DNS0_EU", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "analyzers_manager", + "0002_0024_analyzer_config_cymru_hash_registry_get_observable", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0026_analyzer_config_dns0_eu_malicious_detector.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0026_analyzer_config_dns0_eu_malicious_detector.py new file mode 100644 index 0000000..9edefeb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0026_analyzer_config_dns0_eu_malicious_detector.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNS0_EU_Malicious_Detector", + "python_module": { + "module": "dns.dns_malicious_detectors.dns0_eu_malicious_detector.DNS0EUMaliciousDetector", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a domain or an url is marked as malicious in DNS0.eu database ([Zero](https://www.dns0.eu/zero) service)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0025_analyzer_config_dns0_eu"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0027_analyzer_config_dns0_names.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0027_analyzer_config_dns0_names.py new file mode 100644 index 0000000..e9daed2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0027_analyzer_config_dns0_names.py @@ -0,0 +1,214 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNS0_names", + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Run advanced searches on billions of current and historical domain names. [API](https://docs.dns0.eu/dns-api/names).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["url", "domain", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "root", + "type": "bool", + "description": "Limit results to root domains.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fuzzy", + "type": "list", + "description": "Apply fuzziness to q. Accepts a comma-separated list of fuzzy algorithms, or all to apply them all.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from", + "type": "str", + "description": "Limit results to names seen after this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "to", + "type": "str", + "description": "Limit results to names seen before this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "not_before", + "type": "str", + "description": "Limit results to names not seen before this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sort", + "type": "str", + "description": "Available sorts are first_seen (the default) or last_seen. Both are descending sorts (most recent first).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "format", + "type": "str", + "description": "Available formats are json and dig. Default format is based on the Accept HTTP header.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_names.DNS0Names", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "offset", + "type": "int", + "description": "Used for pagination.", + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0026_analyzer_config_dns0_eu_malicious_detector"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0028_analyzer_config_dns0_rrsets_data.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0028_analyzer_config_dns0_rrsets_data.py new file mode 100644 index 0000000..2a350e1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0028_analyzer_config_dns0_rrsets_data.py @@ -0,0 +1,248 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNS0_rrsets_data", + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Query billions of current and historical DNS resource records sets. [API](https://docs.dns0.eu/dns-api/rrsets).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["domain", "url", "generic", "ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "direction", + "type": "str", + "description": "Used to dispatch matching direction.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "type", + "type": "list", + "description": "Limit results to certain record types (e.g. type=NS,A,AAAA). Accepts a comma-separated list of DNS record types, either in textual or numeric form.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from", + "type": "str", + "description": "Limit results to records seen after this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "to", + "type": "str", + "description": "Limit results to records seen before this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "not_before", + "type": "str", + "description": "Limit results to records not seen before this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sort", + "type": "str", + "description": "Available sorts are first_seen (the default) or last_seen. Both are descending sorts (most recent first).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "format", + "type": "str", + "description": "Available formats are json, cof or dig. Default format is based on the Accept HTTP header.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "offset", + "type": "int", + "description": "Used for pagination.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_subdomain", + "type": "bool", + "description": "Search for subdomains.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "direction", + "type": "str", + "description": "Used to dispatch matching direction.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "right", + "updated_at": "2024-02-09T10:52:29.693346Z", + "owner": None, + "analyzer_config": "DNS0_rrsets_data", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0027_analyzer_config_dns0_names"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0029_analyzer_config_dns0_rrsets_name.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0029_analyzer_config_dns0_rrsets_name.py new file mode 100644 index 0000000..dd8117c --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0029_analyzer_config_dns0_rrsets_name.py @@ -0,0 +1,248 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNS0_rrsets_name", + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Query billions of current and historical DNS resource records sets. [API](https://docs.dns0.eu/dns-api/rrsets).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "direction", + "type": "str", + "description": "Used to dispatch matching direction.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "type", + "type": "list", + "description": "Limit results to certain record types (e.g. type=NS,A,AAAA). Accepts a comma-separated list of DNS record types, either in textual or numeric form.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from", + "type": "str", + "description": "Limit results to records seen after this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "to", + "type": "str", + "description": "Limit results to records seen before this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "not_before", + "type": "str", + "description": "Limit results to records not seen before this date.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sort", + "type": "str", + "description": "Available sorts are first_seen (the default) or last_seen. Both are descending sorts (most recent first).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "format", + "type": "str", + "description": "Available formats are json, cof or dig. Default format is based on the Accept HTTP header.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "offset", + "type": "int", + "description": "Used for pagination.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_subdomain", + "type": "bool", + "description": "Search for subdomains.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns0.dns0_rrsets.DNS0Rrsets", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "direction", + "type": "str", + "description": "Used to dispatch matching direction.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "left", + "updated_at": "2024-02-09T10:52:29.836727Z", + "owner": None, + "analyzer_config": "DNS0_rrsets_name", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0028_analyzer_config_dns0_rrsets_data"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0030_analyzer_config_dnsdb.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0030_analyzer_config_dnsdb.py new file mode 100644 index 0000000..78bdf37 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0030_analyzer_config_dnsdb.py @@ -0,0 +1,319 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNSDB", + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scan an observable against the Passive DNS Farsight Database (support both v1 and v2 versions). Official [API docs](https://docs.dnsdb.info/dnsdb-apiv2/).", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url", "ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "time", + "type": "dict", + "description": "Time range", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Maximum number of results to retrieve.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rrtype", + "type": "str", + "description": "DNS query type.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "server", + "type": "str", + "description": "DNSDB server.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type. Options: domain (default), rrname-wildcard-left, rrname-wildcard-right, names, rdata-wildcard-left, rdata-wildcard-right", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_version", + "type": "int", + "description": "API version of DNSDB (options: `1` and `2`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "time", + "type": "dict", + "description": "Time range", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": { + "last_after": "", + "first_after": "", + "last_before": "", + "first_before": "", + }, + "updated_at": "2024-02-09T10:52:16.921063Z", + "owner": None, + "analyzer_config": "DNSDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Maximum number of results to retrieve.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10000, + "updated_at": "2024-02-09T10:52:16.937028Z", + "owner": None, + "analyzer_config": "DNSDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rrtype", + "type": "str", + "description": "DNS query type.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.951167Z", + "owner": None, + "analyzer_config": "DNSDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "server", + "type": "str", + "description": "DNSDB server.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "api.dnsdb.info", + "updated_at": "2024-02-09T10:52:16.964906Z", + "owner": None, + "analyzer_config": "DNSDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type. Options: domain (default), rrname-wildcard-left, rrname-wildcard-right, names, rdata-wildcard-left, rdata-wildcard-right", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "domain", + "updated_at": "2024-02-09T10:52:16.977748Z", + "owner": None, + "analyzer_config": "DNSDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnsdb.DNSdb", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_version", + "type": "int", + "description": "API version of DNSDB (options: `1` and `2`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 2, + "updated_at": "2024-02-09T10:52:16.991340Z", + "owner": None, + "analyzer_config": "DNSDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0029_analyzer_config_dns0_rrsets_name"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0031_analyzer_config_dnstwist.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0031_analyzer_config_dnstwist.py new file mode 100644 index 0000000..88fe06b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0031_analyzer_config_dnstwist.py @@ -0,0 +1,336 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNStwist", + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scans for potentially malicious permutations of a domain name", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "mxcheck", + "type": "bool", + "description": "Find suspicious mail servers and flag them with SPYING-MX string.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "tld_dict", + "type": "str", + "description": "Dictionary to use with `tld` argument. Options: `common_tlds.dict/abused_tlds.dict`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fuzzy_hash", + "type": "str", + "description": "Fuzzy Hash to use to detect similarities. Options `ssdeep/tlsh`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": "User Agent used to connect to sites", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "nameservers", + "type": "str", + "description": "Alternative DNS servers to use. Add them separated by commas", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "language_dict", + "type": "str", + "description": "Dictionary to use with `dictionary` argument. Options: `english.dict/french.dict/polish.dict`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fuzzy_hash_url", + "type": "str", + "description": "Override URL to fetch the original web page from", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "mxcheck", + "type": "bool", + "description": "Find suspicious mail servers and flag them with SPYING-MX string.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.327136Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "tld_dict", + "type": "str", + "description": "Dictionary to use with `tld` argument. Options: `common_tlds.dict/abused_tlds.dict`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.339816Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fuzzy_hash", + "type": "str", + "description": "Fuzzy Hash to use to detect similarities. Options `ssdeep/tlsh`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "ssdeep", + "updated_at": "2024-02-09T10:52:16.352844Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": "User Agent used to connect to sites", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.34", + "updated_at": "2024-02-09T10:52:16.365784Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "nameservers", + "type": "str", + "description": "Alternative DNS servers to use. Add them separated by commas", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.379533Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "language_dict", + "type": "str", + "description": "Dictionary to use with `dictionary` argument. Options: `english.dict/french.dict/polish.dict`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.391896Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dnstwist.DNStwist", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fuzzy_hash_url", + "type": "str", + "description": "Override URL to fetch the original web page from", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.404265Z", + "owner": None, + "analyzer_config": "DNStwist", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0030_analyzer_config_dnsdb"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0032_analyzer_config_dehashed_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0032_analyzer_config_dehashed_search.py new file mode 100644 index 0000000..f899eab --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0032_analyzer_config_dehashed_search.py @@ -0,0 +1,215 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Dehashed_Search", + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Query against Dehashed's search API. For configuration, refer to the 'Sizing & Pagination' section in [dehashed docs](https://www.dehashed.com/docs).", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "long", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "size", + "type": "int", + "description": "Number of records fetched. Recommend change to a large value.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "pages", + "type": "int", + "description": "Number of pages fetched. Recommend to keep at 1 only to save on credits.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "operator", + "type": "str", + "description": "Search Operator to use among (id, email, ip_address, username, password, hashed_password, hash_type, name, vin, address, phone, database_name). Default: username", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Combination of your dehashed account's email and API key. Format: `email:api-key`.", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "size", + "type": "int", + "description": "Number of records fetched. Recommend change to a large value.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 100, + "updated_at": "2024-02-09T10:52:16.817589Z", + "owner": None, + "analyzer_config": "Dehashed_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "pages", + "type": "int", + "description": "Number of pages fetched. Recommend to keep at 1 only to save on credits.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:16.832848Z", + "owner": None, + "analyzer_config": "Dehashed_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dehashed.DehashedSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "operator", + "type": "str", + "description": "Search Operator to use among (id, email, ip_address, username, password, hashed_password, hash_type, name, vin, address, phone, database_name). Default: username", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "username", + "updated_at": "2024-02-09T10:52:16.866831Z", + "owner": None, + "analyzer_config": "Dehashed_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0031_analyzer_config_dnstwist"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0033_analyzer_config_docguard_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0033_analyzer_config_docguard_get.py new file mode 100644 index 0000000..d396e2b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0033_analyzer_config_docguard_get.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DocGuard_Get", + "python_module": { + "module": "docguard_get.DocGuard_Hash", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if an hash was analyzed on DocGuard. [DocGuard](https://www.docguard.io)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "docguard_get.DocGuard_Hash", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0032_analyzer_config_dehashed_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0034_analyzer_config_docguard_upload_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0034_analyzer_config_docguard_upload_file.py new file mode 100644 index 0000000..88b981d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0034_analyzer_config_docguard_upload_file.py @@ -0,0 +1,135 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DocGuard_Upload_File", + "python_module": { + "module": "docguard.DocGuardUpload", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Analyze office files in seconds. [DocGuard](https://www.docguard.io) Please register and use api-key to your privacy", + "disabled": False, + "soft_time_limit": 180, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": [ + "application/msword", + "application/vnd.ms-office", + "application/vnd.ms-excel.addin.macroEnabled", + "application/x-mspublisher", + "application/vnd.ms-powerpoint", + "application/vnd.ms-excel", + "application/vnd.ms-excel.sheet.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/onenote", + "text/x-ms-iqy", + "application/excel", + "text/xml", + "application/xml", + "application/zip", + "application/encrypted", + "text/plain", + "text/csv", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "docguard.DocGuardUpload", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0033_analyzer_config_docguard_get"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0035_analyzer_config_doc_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0035_analyzer_config_doc_info.py new file mode 100644 index 0000000..159cdf3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0035_analyzer_config_doc_info.py @@ -0,0 +1,158 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Doc_Info", + "python_module": { + "module": "doc_info.DocInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "static Microsoft Office document analysis ([Oletools](https://github.com/decalage2/oletools)) with features to analyze XLM macros, encrypted macros and much more", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/msword", + "application/vnd.ms-office", + "application/vnd.ms-excel.addin.macroEnabled", + "application/x-mspublisher", + "application/vnd.ms-powerpoint", + "application/vnd.ms-excel", + "application/vnd.ms-excel.sheet.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/onenote", + "text/x-ms-iqy", + "application/excel", + "text/xml", + "application/xml", + "application/zip", + "application/encrypted", + "text/plain", + "text/csv", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "doc_info.DocInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "additional_passwords_to_check", + "type": "list", + "description": "List of passwords to try when decrypting the document", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "doc_info.DocInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "additional_passwords_to_check", + "type": "list", + "description": "List of passwords to try when decrypting the document", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [""], + "updated_at": "2024-02-09T10:52:17.025535Z", + "owner": None, + "analyzer_config": "Doc_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0034_analyzer_config_docguard_upload_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0036_analyzer_config_dragonfly_emulation.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0036_analyzer_config_dragonfly_emulation.py new file mode 100644 index 0000000..675dab9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0036_analyzer_config_dragonfly_emulation.py @@ -0,0 +1,292 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Dragonfly_Emulation", + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Emulate malware against [Dragonfly](https://dragonfly.certego.net/?utm_source=intelowl) sandbox by [Certego S.R.L](https://certego.net).", + "disabled": False, + "soft_time_limit": 400, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec", "application/octet-stream"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "root", + "type": "bool", + "description": "If `true`, emulate with root permissions", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private", + "type": "bool", + "description": "If `true`, mark the analysis as private so it's accessible to you and members within your organization only", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profiles", + "type": "list", + "description": "List of profile indices for emulators. Refer to [profiles list](https://dragonfly.certego.net/dashboard/profiles?utm_source=intelowl).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "allow_actions", + "type": "bool", + "description": "If `true`, run actions when a rule matches", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "operating_system", + "type": "str", + "description": "Enum: `WINDOW`|`LINUX`| or leave blank string for automatic detection", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Dragonfly API key. Generate [here](https://dragonfly.certego.net/me/sessions?utm_source=intelowl).", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "Dragonfly instance URL. Don't change this.", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "root", + "type": "bool", + "description": "If `true`, emulate with root permissions", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:17.095304Z", + "owner": None, + "analyzer_config": "Dragonfly_Emulation", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private", + "type": "bool", + "description": "If `true`, mark the analysis as private so it's accessible to you and members within your organization only", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:17.106896Z", + "owner": None, + "analyzer_config": "Dragonfly_Emulation", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profiles", + "type": "list", + "description": "List of profile indices for emulators. Refer to [profiles list](https://dragonfly.certego.net/dashboard/profiles?utm_source=intelowl).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [1, 2], + "updated_at": "2024-02-09T10:52:17.119397Z", + "owner": None, + "analyzer_config": "Dragonfly_Emulation", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "allow_actions", + "type": "bool", + "description": "If `true`, run actions when a rule matches", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:17.131627Z", + "owner": None, + "analyzer_config": "Dragonfly_Emulation", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "dragonfly.DragonflyEmulation", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "operating_system", + "type": "str", + "description": "Enum: `WINDOW`|`LINUX`| or leave blank string for automatic detection", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:17.143946Z", + "owner": None, + "analyzer_config": "Dragonfly_Emulation", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0035_analyzer_config_doc_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0037_analyzer_config_elf_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0037_analyzer_config_elf_info.py new file mode 100644 index 0000000..5ad3ea3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0037_analyzer_config_elf_info.py @@ -0,0 +1,107 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "ELF_Info", + "python_module": { + "module": "elf_info.ELFInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "static ELF analysis", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/x-sharedlib", + "application/x-elf", + "application/x-executable", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0036_analyzer_config_dragonfly_emulation"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0038_analyzer_config_emailrep.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0038_analyzer_config_emailrep.py new file mode 100644 index 0000000..4d92142 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0038_analyzer_config_emailrep.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "EmailRep", + "python_module": { + "module": "emailrep.EmailRep", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Get email reputation from [emailrep.io](https://emailrep.io)", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "emailrep.EmailRep", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0037_analyzer_config_elf_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0039_analyzer_config_filescan_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0039_analyzer_config_filescan_search.py new file mode 100644 index 0000000..a60f813 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0039_analyzer_config_filescan_search.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "FileScan_Search", + "python_module": { + "module": "filescan_search.FileScanSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Finds reports and uploaded files by various tokens, like hash, filename, verdict, IOCs etc via [FileScan.io API](https://www.filescan.io/api/docs).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["url", "domain", "ip", "generic", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "filescan_search.FileScanSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key", + "type": "str", + "description": "Api key", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0038_analyzer_config_emailrep"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0040_analyzer_config_filescan_upload_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0040_analyzer_config_filescan_upload_file.py new file mode 100644 index 0000000..9e4f061 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0040_analyzer_config_filescan_upload_file.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "FileScan_Upload_File", + "python_module": { + "module": "filescan.FileScanUpload", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Extract IoCs from executable files, documents and scripts via [FileScan.io API](https://www.filescan.io/api/docs).", + "disabled": False, + "soft_time_limit": 180, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "filescan.FileScanUpload", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key", + "type": "str", + "description": "Api key", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0039_analyzer_config_filescan_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0041_analyzer_config_file_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0041_analyzer_config_file_info.py new file mode 100644 index 0000000..59536cc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0041_analyzer_config_file_info.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "File_Info", + "python_module": { + "module": "file_info.FileInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "basic static analysis, extracts metadata (with [Exiftools](https://exiftool.org/)) and hashes", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0040_analyzer_config_filescan_upload_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0042_analyzer_config_firehol_iplist.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0042_analyzer_config_firehol_iplist.py new file mode 100644 index 0000000..a1590b0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0042_analyzer_config_firehol_iplist.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "FireHol_IPList", + "python_module": { + "module": "firehol_iplist.FireHol_IPList", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if an IP is in FireHol's IPList. Refer to [FireHol's IPList](https://iplists.firehol.org/).", + "disabled": False, + "soft_time_limit": 180, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "firehol_iplist.FireHol_IPList", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "list_names", + "type": "list", + "description": "A list of firehol list names.", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "firehol_iplist.FireHol_IPList", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "list_names", + "type": "list", + "description": "A list of firehol list names.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": ["firehol_level1.netset"], + "updated_at": "2024-02-09T10:52:17.183078Z", + "owner": None, + "analyzer_config": "FireHol_IPList", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0041_analyzer_config_file_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0043_analyzer_config_floss.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0043_analyzer_config_floss.py new file mode 100644 index 0000000..0cd8265 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0043_analyzer_config_floss.py @@ -0,0 +1,179 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Floss", + "python_module": { + "module": "floss.Floss", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Advanced String Extraction by FireEye", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec", "application/octet-stream"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "floss.Floss", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "rank_strings", + "type": "dict", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "floss.Floss", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_no_of_strings", + "type": "dict", + "description": "", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "floss.Floss", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "rank_strings", + "type": "dict", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": { + "stack_strings": False, + "static_strings": False, + "decoded_strings": False, + }, + "updated_at": "2024-02-09T10:52:17.195495Z", + "owner": None, + "analyzer_config": "Floss", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "floss.Floss", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_no_of_strings", + "type": "dict", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": { + "stack_strings": 1000, + "static_strings": 1000, + "decoded_strings": 1000, + }, + "updated_at": "2024-02-09T10:52:17.207780Z", + "owner": None, + "analyzer_config": "Floss", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0042_analyzer_config_firehol_iplist"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0044_analyzer_config_googlesafebrowsing.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0044_analyzer_config_googlesafebrowsing.py new file mode 100644 index 0000000..239919c --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0044_analyzer_config_googlesafebrowsing.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "GoogleSafebrowsing", + "python_module": { + "module": "dns.dns_malicious_detectors.googlesf.GoogleSF", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scan an observable against GoogleSafeBrowsing DB", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_malicious_detectors.googlesf.GoogleSF", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0043_analyzer_config_floss"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0045_analyzer_config_googlewebrisk.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0045_analyzer_config_googlewebrisk.py new file mode 100644 index 0000000..90485f7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0045_analyzer_config_googlewebrisk.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "GoogleWebRisk", + "python_module": { + "module": "dns.dns_malicious_detectors.google_webrisk.WebRisk", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scan an observable against Web Risk API", + "disabled": False, + "soft_time_limit": 20, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_malicious_detectors.google_webrisk.WebRisk", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "service_account_json", + "type": "dict", + "description": "service account file in JSON format", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0044_analyzer_config_googlesafebrowsing"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0046_analyzer_config_google_dns.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0046_analyzer_config_google_dns.py new file mode 100644 index 0000000..ce8a21a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0046_analyzer_config_google_dns.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Google_DNS", + "python_module": { + "module": "dns.dns_resolvers.google_dns_resolver.GoogleDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Retrieve current domain resolution with Google DoH (DNS over HTTPS)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_resolvers.google_dns_resolver.GoogleDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns.dns_resolvers.google_dns_resolver.GoogleDNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "A", + "updated_at": "2024-02-09T10:52:16.766480Z", + "owner": None, + "analyzer_config": "Google_DNS", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0045_analyzer_config_googlewebrisk"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0047_analyzer_config_greedybear.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0047_analyzer_config_greedybear.py new file mode 100644 index 0000000..98b881b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0047_analyzer_config_greedybear.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "GreedyBear", + "python_module": { + "module": "greedybear.GreedyBear", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP or a domain against the [GreedyBear](https://www.honeynet.org/2021/12/27/new-project-available-greedybear/) service", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "greedybear.GreedyBear", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url", + "type": "str", + "description": "URL of the GreedyBear instance you want to connect to", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "greedybear.GreedyBear", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key required for authentication", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "greedybear.GreedyBear", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url", + "type": "str", + "description": "URL of the GreedyBear instance you want to connect to", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "https://greedybear.honeynet.org", + "updated_at": "2024-02-09T10:52:17.219693Z", + "owner": None, + "analyzer_config": "GreedyBear", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0046_analyzer_config_google_dns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0048_analyzer_config_greynoise.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0048_analyzer_config_greynoise.py new file mode 100644 index 0000000..17cc632 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0048_analyzer_config_greynoise.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "GreyNoise", + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP against the Greynoise API (requires API key)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "greynoise_api_version", + "type": "str", + "description": "[GreyNoise Enterprise API](https://docs.greynoise.io/docs/using-the-greynoise-api)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "greynoise_api_version", + "type": "str", + "description": "[GreyNoise Enterprise API](https://docs.greynoise.io/docs/using-the-greynoise-api)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "v2", + "updated_at": "2024-02-09T10:52:17.238677Z", + "owner": None, + "analyzer_config": "GreyNoise", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0047_analyzer_config_greedybear"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0049_analyzer_config_greynoisecommunity.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0049_analyzer_config_greynoisecommunity.py new file mode 100644 index 0000000..1fb55ac --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0049_analyzer_config_greynoisecommunity.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "GreyNoiseCommunity", + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP against the Community Greynoise API", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "greynoise_api_version", + "type": "str", + "description": "[GreyNoise Enterprise API](https://docs.greynoise.io/docs/using-the-greynoise-api)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "greynoiseintel.GreyNoiseAnalyzer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "greynoise_api_version", + "type": "str", + "description": "[GreyNoise Enterprise API](https://docs.greynoise.io/docs/using-the-greynoise-api)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "v3", + "updated_at": "2024-02-09T10:52:17.256525Z", + "owner": None, + "analyzer_config": "GreyNoiseCommunity", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0048_analyzer_config_greynoise"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0050_analyzer_config_hashlookupserver_get_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0050_analyzer_config_hashlookupserver_get_file.py new file mode 100644 index 0000000..c8b5f61 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0050_analyzer_config_hashlookupserver_get_file.py @@ -0,0 +1,157 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "HashLookupServer_Get_File", + "python_module": { + "module": "hashlookup.HashLookupServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if a md5 or sha1 is available in the database of known file hosted by CIRCL", + "disabled": False, + "soft_time_limit": 40, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": [ + "application/msword", + "application/vnd.ms-office", + "application/vnd.ms-excel.addin.macroEnabled", + "application/x-mspublisher", + "application/vnd.ms-powerpoint", + "application/vnd.ms-excel", + "application/vnd.ms-excel.sheet.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/onenote", + "text/x-ms-iqy", + "application/excel", + "text/xml", + "application/xml", + "application/zip", + "application/encrypted", + "application/vnd.tcpdump.pcap", + ], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "hashlookup.HashLookupServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "hashlookup_server", + "type": "str", + "description": "custom hashlookup-server", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "hashlookup.HashLookupServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "hashlookup_server", + "type": "str", + "description": "custom hashlookup-server", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:17.082613Z", + "owner": None, + "analyzer_config": "HashLookupServer_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0049_analyzer_config_greynoisecommunity"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0051_analyzer_config_hashlookupserver_get_observable.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0051_analyzer_config_hashlookupserver_get_observable.py new file mode 100644 index 0000000..690545f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0051_analyzer_config_hashlookupserver_get_observable.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "HashLookupServer_Get_Observable", + "python_module": { + "module": "hashlookup.HashLookupServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if a md5 or sha1 is available in the database of known file hosted by CIRCL", + "disabled": False, + "soft_time_limit": 20, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "hashlookup.HashLookupServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "hashlookup_server", + "type": "str", + "description": "custom hashlookup-server", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "hashlookup.HashLookupServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "hashlookup_server", + "type": "str", + "description": "custom hashlookup-server", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:17.281119Z", + "owner": None, + "analyzer_config": "HashLookupServer_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0050_analyzer_config_hashlookupserver_get_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0052_analyzer_config_haveibeenpwned.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0052_analyzer_config_haveibeenpwned.py new file mode 100644 index 0000000..87ee2c2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0052_analyzer_config_haveibeenpwned.py @@ -0,0 +1,218 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "HaveIBeenPwned", + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if an email address has been involved in a data breach", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "GREEN", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "domain", + "type": "str", + "description": "Search for data breaches specific to a domain", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "truncate_response", + "type": "bool", + "description": "Truncate response to only include most recent data breaches", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_unverified", + "type": "bool", + "description": "Include unverified data breaches in the response", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API Key for HaveIBeenPwned", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "domain", + "type": "str", + "description": "Search for data breaches specific to a domain", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:17.311579Z", + "owner": None, + "analyzer_config": "HaveIBeenPwned", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "truncate_response", + "type": "bool", + "description": "Truncate response to only include most recent data breaches", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:17.323627Z", + "owner": None, + "analyzer_config": "HaveIBeenPwned", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "haveibeenpwned.HaveIBeenPwned", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_unverified", + "type": "bool", + "description": "Include unverified data breaches in the response", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:17.337237Z", + "owner": None, + "analyzer_config": "HaveIBeenPwned", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "analyzers_manager", + "0002_0051_analyzer_config_hashlookupserver_get_observable", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0053_analyzer_config_honeydb.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0053_analyzer_config_honeydb.py new file mode 100644 index 0000000..a928780 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0053_analyzer_config_honeydb.py @@ -0,0 +1,160 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "HoneyDB", + "python_module": { + "module": "honeydb.HoneyDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "HoneyDB IP lookup service", + "disabled": False, + "soft_time_limit": 200, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "honeydb.HoneyDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "honeydb_analysis", + "type": "str", + "description": "Choose which endpoint to query from the HoneyDB service (options are: `all`, `scan_twitter`, `ip_query`, `ip_history`, `internet_scanner`, `ip_info`)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "honeydb.HoneyDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_id_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "honeydb.HoneyDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "honeydb.HoneyDB", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "honeydb_analysis", + "type": "str", + "description": "Choose which endpoint to query from the HoneyDB service (options are: `all`, `scan_twitter`, `ip_query`, `ip_history`, `internet_scanner`, `ip_info`)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "all", + "updated_at": "2024-02-09T10:52:17.355810Z", + "owner": None, + "analyzer_config": "HoneyDB", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0052_analyzer_config_haveibeenpwned"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0054_analyzer_config_hunter_how.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0054_analyzer_config_hunter_how.py new file mode 100644 index 0000000..0a19f10 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0054_analyzer_config_hunter_how.py @@ -0,0 +1,248 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Hunter_How", + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "[Hunter_How](https://hunter.how/search-api) provides details for a specified ip and domain", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page", + "type": "int", + "description": "The number of displayed page.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "end_time", + "type": "str", + "description": "Only show results after the given date (yyyy-mm-dd)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page_size", + "type": "int", + "description": " The number of results displayed on one page ", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "start_time", + "type": "str", + "description": "Only show results after the given date (yyyy-mm-dd)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for the Hunter_How", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page", + "type": "int", + "description": "The number of displayed page.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:17.381439Z", + "owner": None, + "analyzer_config": "Hunter_How", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "end_time", + "type": "str", + "description": "Only show results after the given date (yyyy-mm-dd)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "2022-12-01", + "updated_at": "2024-02-09T10:52:17.393195Z", + "owner": None, + "analyzer_config": "Hunter_How", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page_size", + "type": "int", + "description": " The number of results displayed on one page ", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:17.406295Z", + "owner": None, + "analyzer_config": "Hunter_How", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "hunter_how.Hunter_How", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "start_time", + "type": "str", + "description": "Only show results after the given date (yyyy-mm-dd)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "2022-01-01", + "updated_at": "2024-02-09T10:52:17.420195Z", + "owner": None, + "analyzer_config": "Hunter_How", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0053_analyzer_config_honeydb"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0055_analyzer_config_hunter_io.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0055_analyzer_config_hunter_io.py new file mode 100644 index 0000000..c8e8d37 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0055_analyzer_config_hunter_io.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Hunter_Io", + "python_module": { + "module": "hunter_io.Hunter_Io", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scans a domain name and returns set of data about the organisation, the email address found and additional information about the people owning those email addresses", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "hunter_io.Hunter_Io", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0054_analyzer_config_hunter_how"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0056_analyzer_config_hybridanalysis_get_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0056_analyzer_config_hybridanalysis_get_file.py new file mode 100644 index 0000000..d54b1c1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0056_analyzer_config_hybridanalysis_get_file.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "HybridAnalysis_Get_File", + "python_module": { + "module": "ha_get.HybridAnalysisGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check file hash on HybridAnalysis sandbox reports", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ha_get.HybridAnalysisGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0055_analyzer_config_hunter_io"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0057_analyzer_config_hybridanalysis_get_observable.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0057_analyzer_config_hybridanalysis_get_observable.py new file mode 100644 index 0000000..d7185b7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0057_analyzer_config_hybridanalysis_get_observable.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "HybridAnalysis_Get_Observable", + "python_module": { + "module": "ha_get.HybridAnalysisGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "search an observable in the HybridAnalysis sandbox reports", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ha_get.HybridAnalysisGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0056_analyzer_config_hybridanalysis_get_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0058_analyzer_config_ipapi.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0058_analyzer_config_ipapi.py new file mode 100644 index 0000000..aeed884 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0058_analyzer_config_ipapi.py @@ -0,0 +1,174 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "IPApi", + "python_module": { + "module": "ipapi.IPApi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Gives information about [IPs](https://ip-api.com/docs/api:batch) and [DNS](https://ip-api.com/docs/dns)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ipapi.IPApi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "lang", + "type": "str", + "description": "specify the response language", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipapi.IPApi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fields", + "type": "str", + "description": "specify the information fields", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "ipapi.IPApi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "lang", + "type": "str", + "description": "specify the response language", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:18.029923Z", + "owner": None, + "analyzer_config": "IPApi", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "ipapi.IPApi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "fields", + "type": "str", + "description": "specify the information fields", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:18.056501Z", + "owner": None, + "analyzer_config": "IPApi", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "analyzers_manager", + "0002_0057_analyzer_config_hybridanalysis_get_observable", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0059_analyzer_config_ipinfo.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0059_analyzer_config_ipinfo.py new file mode 100644 index 0000000..75d3213 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0059_analyzer_config_ipinfo.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "IPInfo", + "python_module": { + "module": "ipinfo.IPInfo", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Brief Information regarding given IP", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ipinfo.IPInfo", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0058_analyzer_config_ipapi"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0060_analyzer_config_inquest_dfi.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0060_analyzer_config_inquest_dfi.py new file mode 100644 index 0000000..2718412 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0060_analyzer_config_inquest_dfi.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "InQuest_DFI", + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Deep File Inspection - search dfi", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "inquest_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "inquest_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "dfi_search", + "updated_at": "2024-02-09T10:52:17.947814Z", + "owner": None, + "analyzer_config": "InQuest_DFI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0059_analyzer_config_ipinfo"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0061_analyzer_config_inquest_iocdb.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0061_analyzer_config_inquest_iocdb.py new file mode 100644 index 0000000..3d09f94 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0061_analyzer_config_inquest_iocdb.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "InQuest_IOCdb", + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Indicators of Compromise Database - search IOCdb", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "inquest_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "inquest_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "iocdb_search", + "updated_at": "2024-02-09T10:52:17.882094Z", + "owner": None, + "analyzer_config": "InQuest_IOCdb", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0060_analyzer_config_inquest_dfi"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0062_analyzer_config_inquest_repdb.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0062_analyzer_config_inquest_repdb.py new file mode 100644 index 0000000..aa0f6cc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0062_analyzer_config_inquest_repdb.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "InQuest_REPdb", + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Reputation Database - search REPdb", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "inquest_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "inquest.InQuest", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "inquest_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "repdb_search", + "updated_at": "2024-02-09T10:52:17.293683Z", + "owner": None, + "analyzer_config": "InQuest_REPdb", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0061_analyzer_config_inquest_iocdb"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0063_analyzer_config_intelx_intelligent_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0063_analyzer_config_intelx_intelligent_search.py new file mode 100644 index 0000000..e740917 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0063_analyzer_config_intelx_intelligent_search.py @@ -0,0 +1,347 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "IntelX_Intelligent_Search", + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Intelligent search against IntelX API", + "disabled": False, + "soft_time_limit": 45, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dateto", + "type": "str", + "description": "use this in combination with 'datefrom' to filter the query", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "datefrom", + "type": "str", + "description": "use this in combination with 'dateto' to filter the query", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query Type. Choose between 'playbook' and 'intelligent'", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rows_limit", + "type": "int", + "description": "max number of results to retrieve", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dateto", + "type": "str", + "description": "use this in combination with 'datefrom' to filter the query", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:17.512269Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:17.528099Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "datefrom", + "type": "str", + "description": "use this in combination with 'dateto' to filter the query", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:17.542050Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:17.559015Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query Type. Choose between 'playbook' and 'intelligent'", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "intelligent", + "updated_at": "2024-02-09T10:52:17.573352Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rows_limit", + "type": "int", + "description": "max number of results to retrieve", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1000, + "updated_at": "2024-02-09T10:52:17.587835Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 3, + "updated_at": "2024-02-09T10:52:17.602356Z", + "owner": None, + "analyzer_config": "IntelX_Intelligent_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0062_analyzer_config_inquest_repdb"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0064_analyzer_config_intelx_phonebook.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0064_analyzer_config_intelx_phonebook.py new file mode 100644 index 0000000..b85c582 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0064_analyzer_config_intelx_phonebook.py @@ -0,0 +1,303 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "IntelX_Phonebook", + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Phonebook alike search against IntelX API", + "disabled": False, + "soft_time_limit": 45, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dateto", + "type": "str", + "description": "use this in combination with 'datefrom' to filter the query", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "datefrom", + "type": "str", + "description": "use this in combination with 'dateto' to filter the query", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query Type. Choose between 'playbook' and 'intelligent'", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rows_limit", + "type": "int", + "description": "max number of results to retrieve", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:17.623898Z", + "owner": None, + "analyzer_config": "IntelX_Phonebook", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:17.641736Z", + "owner": None, + "analyzer_config": "IntelX_Phonebook", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query Type. Choose between 'playbook' and 'intelligent'", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "phonebook", + "updated_at": "2024-02-09T10:52:17.659907Z", + "owner": None, + "analyzer_config": "IntelX_Phonebook", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rows_limit", + "type": "int", + "description": "max number of results to retrieve", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1000, + "updated_at": "2024-02-09T10:52:17.696642Z", + "owner": None, + "analyzer_config": "IntelX_Phonebook", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intelx.IntelX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 3, + "updated_at": "2024-02-09T10:52:17.717201Z", + "owner": None, + "analyzer_config": "IntelX_Phonebook", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0063_analyzer_config_intelx_intelligent_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0065_analyzer_config_intezer_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0065_analyzer_config_intezer_get.py new file mode 100644 index 0000000..e4f930c --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0065_analyzer_config_intezer_get.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Intezer_Get", + "python_module": { + "module": "intezer_get.IntezerGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if an hash was analyzed on Intezer. Register for a free community account [here](https://analyze.intezer.com/sign-in?utm_source=IntelOwl).", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "intezer_get.IntezerGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "soft_time_limit", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intezer_get.IntezerGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "intezer_get.IntezerGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "soft_time_limit", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 100, + "updated_at": "2024-02-09T10:52:17.750440Z", + "owner": None, + "analyzer_config": "Intezer_Get", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0064_analyzer_config_intelx_phonebook"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0066_analyzer_config_intezer_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0066_analyzer_config_intezer_scan.py new file mode 100644 index 0000000..67c26f6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0066_analyzer_config_intezer_scan.py @@ -0,0 +1,241 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Intezer_Scan", + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Scan a file hash on Intezer. Register for a free community account [here](https://analyze.intezer.com/sign-in?utm_source=IntelOwl). With TLP `CLEAR`, in case the hash is not found, you would send the file to the service.", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [ + "application/x-dosexec", + "application/x-executable", + "application/x-elf", + "application/zip", + "application/java-archive", + "application/vnd.android.package-archive", + "application/x-dex", + "application/msword", + "application/vnd.ms-office", + "application/vnd.ms-excel.addin.macroEnabled", + "application/x-mspublisher", + "application/vnd.ms-powerpoint", + "application/vnd.ms-excel", + "application/vnd.ms-excel.sheet.macroEnabled.12", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "application/onenote", + "text/x-ms-iqy", + "application/excel", + "text/xml", + "application/xml", + "application/encrypted", + "text/plain", + "text/csv", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "soft_time_limit", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "disable_static_unpacking", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "disable_dynamic_unpacking", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "soft_time_limit", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 300, + "updated_at": "2024-02-09T10:52:17.787199Z", + "owner": None, + "analyzer_config": "Intezer_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "disable_static_unpacking", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:17.803565Z", + "owner": None, + "analyzer_config": "Intezer_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "intezer_scan.IntezerScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "disable_dynamic_unpacking", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:17.827212Z", + "owner": None, + "analyzer_config": "Intezer_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0065_analyzer_config_intezer_get"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0067_analyzer_config_koodous.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0067_analyzer_config_koodous.py new file mode 100644 index 0000000..c3b1e9f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0067_analyzer_config_koodous.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Koodous", + "python_module": { + "module": "koodous.Koodous", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Get information about android malware", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "long", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "koodous.Koodous", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API Key for the Koodous analyzer", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0066_analyzer_config_intezer_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0068_analyzer_config_misp.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0068_analyzer_config_misp.py new file mode 100644 index 0000000..028a990 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0068_analyzer_config_misp.py @@ -0,0 +1,391 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MISP", + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable on a custom MISP instance", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results returned", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from_days", + "type": "int", + "description": "Check only events created in the past X days. 0 for no filter", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "strict_search", + "type": "bool", + "description": "Search strictly on the observable value (True) or search on attributes containing observable value (False)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter_on_type", + "type": "bool", + "description": "Filter the search on the type of the observable.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enforce_warninglist", + "type": "bool", + "description": "Remove any attributes from the result that would cause a hit on a warninglist entry.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.092259Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results returned", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 50, + "updated_at": "2024-02-09T10:52:18.107503Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from_days", + "type": "int", + "description": "Check only events created in the past X days. 0 for no filter", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 90, + "updated_at": "2024-02-09T10:52:18.122488Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.138551Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "strict_search", + "type": "bool", + "description": "Search strictly on the observable value (True) or search on attributes containing observable value (False)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.152993Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter_on_type", + "type": "bool", + "description": "Filter the search on the type of the observable.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.167009Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enforce_warninglist", + "type": "bool", + "description": "Remove any attributes from the result that would cause a hit on a warninglist entry.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.179483Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.206089Z", + "owner": None, + "analyzer_config": "MISP", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0067_analyzer_config_koodous"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0069_analyzer_config_mispfirst.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0069_analyzer_config_mispfirst.py new file mode 100644 index 0000000..f516e2f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0069_analyzer_config_mispfirst.py @@ -0,0 +1,391 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MISPFIRST", + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable on the FIRST MISP instance", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results returned", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from_days", + "type": "int", + "description": "Check only events created in the past X days. 0 for no filter", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "strict_search", + "type": "bool", + "description": "Search strictly on the observable value (True) or search on attributes containing observable value (False)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter_on_type", + "type": "bool", + "description": "Filter the search on the type of the observable.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enforce_warninglist", + "type": "bool", + "description": "Remove any attributes from the result that would cause a hit on a warninglist entry.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.259306Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results returned", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 50, + "updated_at": "2024-02-09T10:52:18.315038Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from_days", + "type": "int", + "description": "Check only events created in the past X days. 0 for no filter", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 90, + "updated_at": "2024-02-09T10:52:18.362486Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.442729Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "strict_search", + "type": "bool", + "description": "Search strictly on the observable value (True) or search on attributes containing observable value (False)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.526707Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter_on_type", + "type": "bool", + "description": "Filter the search on the type of the observable.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.567578Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enforce_warninglist", + "type": "bool", + "description": "Remove any attributes from the result that would cause a hit on a warninglist entry.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.628456Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.675306Z", + "owner": None, + "analyzer_config": "MISPFIRST", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0068_analyzer_config_misp"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0070_analyzer_config_mispfirst_check_hash.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0070_analyzer_config_mispfirst_check_hash.py new file mode 100644 index 0000000..7f34b41 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0070_analyzer_config_mispfirst_check_hash.py @@ -0,0 +1,214 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MISPFIRST_Check_Hash", + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check a file hash on the FIRST MISP instance", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results returned", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from_days", + "type": "int", + "description": "Check only events created in the past X days. 0 for no filter", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "strict_search", + "type": "bool", + "description": "Search strictly on the observable value (True) or search on attributes containing observable value (False)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter_on_type", + "type": "bool", + "description": "Filter the search on the type of the observable.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enforce_warninglist", + "type": "bool", + "description": "Remove any attributes from the result that would cause a hit on a warninglist entry.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0069_analyzer_config_mispfirst"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0071_analyzer_config_misp_check_hash.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0071_analyzer_config_misp_check_hash.py new file mode 100644 index 0000000..26f2961 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0071_analyzer_config_misp_check_hash.py @@ -0,0 +1,214 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MISP_Check_Hash", + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check a file hash on a MISP instance", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Limit the number of results returned", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "from_days", + "type": "int", + "description": "Check only events created in the past X days. 0 for no filter", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "strict_search", + "type": "bool", + "description": "Search strictly on the observable value (True) or search on attributes containing observable value (False)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter_on_type", + "type": "bool", + "description": "Filter the search on the type of the observable.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enforce_warninglist", + "type": "bool", + "description": "Remove any attributes from the result that would cause a hit on a warninglist entry.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0070_analyzer_config_mispfirst_check_hash"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0072_analyzer_config_mwdb_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0072_analyzer_config_mwdb_get.py new file mode 100644 index 0000000..ba15545 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0072_analyzer_config_mwdb_get.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MWDB_Get", + "python_module": { + "module": "mwdb_get.MWDBGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if an hash was analyzed on MWDB", + "disabled": False, + "soft_time_limit": 20, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "mwdb_get.MWDBGet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0071_analyzer_config_misp_check_hash"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0073_analyzer_config_mwdb_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0073_analyzer_config_mwdb_scan.py new file mode 100644 index 0000000..b8cd647 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0073_analyzer_config_mwdb_scan.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MWDB_Scan", + "python_module": { + "module": "mwdb_scan.MWDB_Scan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Check a file hash against [MWDB by Cert Polska](https://mwdb.cert.pl/). With TLP `CLEAR`, in case the hash is not found, you would send the file to the service.", + "disabled": False, + "soft_time_limit": 400, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "mwdb_scan.MWDB_Scan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "mwdb_scan.MWDB_Scan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "mwdb_scan.MWDB_Scan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "Number of retries to perform for polling analysis results.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "mwdb_scan.MWDB_Scan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.786390Z", + "owner": None, + "analyzer_config": "MWDB_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "mwdb_scan.MWDB_Scan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "Number of retries to perform for polling analysis results.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 50, + "updated_at": "2024-02-09T10:52:18.803495Z", + "owner": None, + "analyzer_config": "MWDB_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0072_analyzer_config_mwdb_get"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0074_analyzer_config_malpedia_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0074_analyzer_config_malpedia_scan.py new file mode 100644 index 0000000..8116317 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0074_analyzer_config_malpedia_scan.py @@ -0,0 +1,127 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Malpedia_Scan", + "python_module": { + "module": "malpedia_scan.MalpediaScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "scan a binary or a zip file (pwd:infected) against all the yara rules available in Malpedia", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": [ + "application/x-binary", + "application/x-dosexec", + "application/x-executable", + "application/x-elf", + "application/x-macbinary", + "application/mac-binary", + "application/octet-stream", + "application/zip", + "application/x-zip-compressed", + "application/x-compressed", + "multipart/x-zip", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "malpedia_scan.MalpediaScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0073_analyzer_config_mwdb_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0075_analyzer_config_malwarebazaar_get_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0075_analyzer_config_malwarebazaar_get_file.py new file mode 100644 index 0000000..75a890f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0075_analyzer_config_malwarebazaar_get_file.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MalwareBazaar_Get_File", + "python_module": { + "module": "mb_get.MB_GET", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a particular malware sample is known to MalwareBazaar", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0074_analyzer_config_malpedia_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0076_analyzer_config_malwarebazaar_get_observable.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0076_analyzer_config_malwarebazaar_get_observable.py new file mode 100644 index 0000000..a0e51d9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0076_analyzer_config_malwarebazaar_get_observable.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MalwareBazaar_Get_Observable", + "python_module": { + "module": "mb_get.MB_GET", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a particular malware hash is known to MalwareBazaar", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0075_analyzer_config_malwarebazaar_get_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0077_analyzer_config_malwarebazaar_google_observable.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0077_analyzer_config_malwarebazaar_google_observable.py new file mode 100644 index 0000000..b50c866 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0077_analyzer_config_malwarebazaar_google_observable.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MalwareBazaar_Google_Observable", + "python_module": { + "module": "mb_google.MB_GOOGLE", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a particular IP, domain or url is known to MalwareBazaar using google search", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0076_analyzer_config_malwarebazaar_get_observable"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0078_analyzer_config_maxmindgeoip.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0078_analyzer_config_maxmindgeoip.py new file mode 100644 index 0000000..308b90e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0078_analyzer_config_maxmindgeoip.py @@ -0,0 +1,140 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MaxMindGeoIP", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "0", + "hour": "1", + "day_of_week": "3", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "0", + "hour": "1", + "day_of_week": "3", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "api_app.analyzers_manager.observable_analyzers.maxmind.MaxmindUpdate", + "task": "intel_owl.tasks.update", + "kwargs": '{"python_module_pk": 64}', + "queue": "default", + "enabled": True, + }, + "module": "maxmind.Maxmind", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "extract GeoIP info for an IP", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "maxmind.Maxmind", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key to access the service", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "analyzers_manager", + "0002_0077_analyzer_config_malwarebazaar_google_observable", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0079_analyzer_config_mnemonic_passivedns.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0079_analyzer_config_mnemonic_passivedns.py new file mode 100644 index 0000000..158363a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0079_analyzer_config_mnemonic_passivedns.py @@ -0,0 +1,171 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Mnemonic_PassiveDNS", + "python_module": { + "module": "mnemonic_pdns.MnemonicPassiveDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Look up a domain or IP using the [Mnemonic PassiveDNS public API](https://docs.mnemonic.no/display/public/API/Passive+DNS+Overview).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "mnemonic_pdns.MnemonicPassiveDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Number of records to fetch.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "mnemonic_pdns.MnemonicPassiveDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "cof_format", + "type": "bool", + "description": "Return result in the PassiveDNS [Common Output Format](https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/).", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "mnemonic_pdns.MnemonicPassiveDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "Number of records to fetch.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1000, + "updated_at": "2024-02-09T10:52:18.988658Z", + "owner": None, + "analyzer_config": "Mnemonic_PassiveDNS", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "mnemonic_pdns.MnemonicPassiveDNS", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "cof_format", + "type": "bool", + "description": "Return result in the PassiveDNS [Common Output Format](https://datatracker.ietf.org/doc/draft-dulaunoy-dnsop-passive-dns-cof/).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.002334Z", + "owner": None, + "analyzer_config": "Mnemonic_PassiveDNS", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0078_analyzer_config_maxmindgeoip"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0080_analyzer_config_netlas.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0080_analyzer_config_netlas.py new file mode 100644 index 0000000..599bd7d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0080_analyzer_config_netlas.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Netlas", + "python_module": { + "module": "netlas.Netlas", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "[Netlas API](https://netlas.io/api) provides accurate technical information on IP addresses.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "netlas.Netlas", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for the netlas analyzer", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0079_analyzer_config_mnemonic_passivedns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0081_analyzer_config_onyphe.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0081_analyzer_config_onyphe.py new file mode 100644 index 0000000..56bb73c --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0081_analyzer_config_onyphe.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "ONYPHE", + "python_module": { + "module": "onyphe.Onyphe", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "search an observable in the ONYPHE", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "onyphe.Onyphe", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0080_analyzer_config_netlas"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0082_analyzer_config_otxquery.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0082_analyzer_config_otxquery.py new file mode 100644 index 0000000..21add23 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0082_analyzer_config_otxquery.py @@ -0,0 +1,248 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "OTXQuery", + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable on Alienvault OTX", + "disabled": False, + "soft_time_limit": 90, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Timeout of the request", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sections", + "type": "list", + "description": "Sections to download. Options: [general, reputation, geo, malware, url_list, passive_dns, analysis", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "full_analysis", + "type": "bool", + "description": "download all the available sections for the observable type", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Timeout of the request", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:18.925612Z", + "owner": None, + "analyzer_config": "OTXQuery", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.940486Z", + "owner": None, + "analyzer_config": "OTXQuery", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sections", + "type": "list", + "description": "Sections to download. Options: [general, reputation, geo, malware, url_list, passive_dns, analysis", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": ["general"], + "updated_at": "2024-02-09T10:52:18.954022Z", + "owner": None, + "analyzer_config": "OTXQuery", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "full_analysis", + "type": "bool", + "description": "download all the available sections for the observable type", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.967318Z", + "owner": None, + "analyzer_config": "OTXQuery", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0081_analyzer_config_onyphe"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0083_analyzer_config_otx_check_hash.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0083_analyzer_config_otx_check_hash.py new file mode 100644 index 0000000..8dfd8e9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0083_analyzer_config_otx_check_hash.py @@ -0,0 +1,248 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "OTX_Check_Hash", + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check file hash on OTX Alienvault", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Timeout of the request", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sections", + "type": "list", + "description": "Sections to download. Options: [general, reputation, geo, malware, url_list, passive_dns, analysis", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "full_analysis", + "type": "bool", + "description": "download all the available sections for the observable type", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Timeout of the request", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:16.449303Z", + "owner": None, + "analyzer_config": "OTX_Check_Hash", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:16.461088Z", + "owner": None, + "analyzer_config": "OTX_Check_Hash", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sections", + "type": "list", + "description": "Sections to download. Options: [general, reputation, geo, malware, url_list, passive_dns, analysis", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": ["general"], + "updated_at": "2024-02-09T10:52:16.474347Z", + "owner": None, + "analyzer_config": "OTX_Check_Hash", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "otx.OTX", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "full_analysis", + "type": "bool", + "description": "download all the available sections for the observable type", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:16.487714Z", + "owner": None, + "analyzer_config": "OTX_Check_Hash", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0082_analyzer_config_otxquery"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0084_analyzer_config_onenote_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0084_analyzer_config_onenote_info.py new file mode 100644 index 0000000..31eb3b9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0084_analyzer_config_onenote_info.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "OneNote_Info", + "python_module": { + "module": "onenote.OneNoteInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Extracting information from OneNote Office docs via [PyOneNote](https://github.com/DissectMalware/pyOneNote)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/onenote"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0083_analyzer_config_otx_check_hash"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0085_analyzer_config_onionscan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0085_analyzer_config_onionscan.py new file mode 100644 index 0000000..5af95f7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0085_analyzer_config_onionscan.py @@ -0,0 +1,171 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Onionscan", + "python_module": { + "module": "onionscan.Onionscan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scans TOR .onion domains for privacy leaks and information disclosures.", + "disabled": False, + "soft_time_limit": 720, + "routing_key": "long", + "health_check_status": True, + "type": "observable", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": ["domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "onionscan.Onionscan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "Verbose output", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "onionscan.Onionscan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "tor_proxy_address", + "type": "str", + "description": "Tor proxy address", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "onionscan.Onionscan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verbose", + "type": "bool", + "description": "Verbose output", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.841195Z", + "owner": None, + "analyzer_config": "Onionscan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "onionscan.Onionscan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "tor_proxy_address", + "type": "str", + "description": "Tor proxy address", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.857281Z", + "owner": None, + "analyzer_config": "Onionscan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0084_analyzer_config_onenote_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0086_analyzer_config_opencti.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0086_analyzer_config_opencti.py new file mode 100644 index 0000000..e059317 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0086_analyzer_config_opencti.py @@ -0,0 +1,226 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "OpenCTI", + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable on a custom OpenCTI instance. CARE! This may require additional advanced configuration. Check the docs [here](https://intelowl.readthedocs.io/en/latest/Advanced-Configuration.html#opencti)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "proxies", + "type": "dict", + "description": "Use these options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_verify", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your OpenCTI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "exact_search", + "type": "bool", + "description": "Use this if you want exact matches only for the observables returned.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for your OpenCTI instance", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "URL of your OpenCTI instance", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "proxies", + "type": "dict", + "description": "Use these options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": {"http": "", "https": ""}, + "updated_at": "2024-02-09T10:52:18.855188Z", + "owner": None, + "analyzer_config": "OpenCTI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ssl_verify", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your OpenCTI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:18.873566Z", + "owner": None, + "analyzer_config": "OpenCTI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "exact_search", + "type": "bool", + "description": "Use this if you want exact matches only for the observables returned.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:18.892037Z", + "owner": None, + "analyzer_config": "OpenCTI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0085_analyzer_config_onionscan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0087_analyzer_config_pdf_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0087_analyzer_config_pdf_info.py new file mode 100644 index 0000000..be89e91 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0087_analyzer_config_pdf_info.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "PDF_Info", + "python_module": { + "module": "pdf_info.PDFInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "static PDF analysis (peepdf + pdfid)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/pdf"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0086_analyzer_config_opencti"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0088_analyzer_config_pe_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0088_analyzer_config_pe_info.py new file mode 100644 index 0000000..17460b7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0088_analyzer_config_pe_info.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "PE_Info", + "python_module": { + "module": "pe_info.PEInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "static PE analysis (pefile)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0087_analyzer_config_pdf_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0089_analyzer_config_peframe_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0089_analyzer_config_peframe_scan.py new file mode 100644 index 0000000..8c8f20e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0089_analyzer_config_peframe_scan.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "PEframe_Scan", + "python_module": { + "module": "peframe.PEframe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Perform static analysis on Portable Executable malware and malicious MS Office documents", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0088_analyzer_config_pe_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0090_analyzer_config_phishingarmy.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0090_analyzer_config_phishingarmy.py new file mode 100644 index 0000000..dc3843e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0090_analyzer_config_phishingarmy.py @@ -0,0 +1,125 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "PhishingArmy", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "5", + "hour": "*/6", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "5", + "hour": "*/6", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "api_app.analyzers_manager.observable_analyzers.phishing_army.PhishingArmyUpdate", + "task": "intel_owl.tasks.update", + "kwargs": '{"python_module_pk": 75}', + "queue": "default", + "enabled": True, + }, + "module": "phishing_army.PhishingArmy", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Search an observable in the [PhishingArmy](https://phishing.army/) blocklist", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["url", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0089_analyzer_config_peframe_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0091_analyzer_config_phishstats.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0091_analyzer_config_phishstats.py new file mode 100644 index 0000000..279dde7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0091_analyzer_config_phishstats.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Phishstats", + "python_module": { + "module": "phishstats.PhishStats", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Search PhishStats API to determine if an IP/URL/domain is malicious.", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "GREEN", + "observable_supported": ["ip", "url", "domain", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0090_analyzer_config_phishingarmy"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0092_analyzer_config_phishtank.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0092_analyzer_config_phishtank.py new file mode 100644 index 0000000..18100ea --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0092_analyzer_config_phishtank.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Phishtank", + "python_module": { + "module": "phishtank.Phishtank", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if url is verified in [Phishtank](https://phishtank.com/) API.", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "phishtank.Phishtank", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Optional API key.", + "is_secret": True, + "required": False, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0091_analyzer_config_phishstats"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0093_analyzer_config_pulsedive.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0093_analyzer_config_pulsedive.py new file mode 100644 index 0000000..0e56181 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0093_analyzer_config_pulsedive.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Pulsedive", + "python_module": { + "module": "pulsedive.Pulsedive", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scan indicators and retrieve results from [Pulsedive](https://pulsedive.com/)'s API", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "pulsedive.Pulsedive", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "scan_mode", + "type": "str", + "description": "[basic, passive, active] By Default there is no scan of the observable. You can choose to add either active or passive scanning. See [doc](https://pulsedive.com/api/scan) for more info", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "pulsedive.Pulsedive", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Optional API key.", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "pulsedive.Pulsedive", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "scan_mode", + "type": "str", + "description": "[basic, passive, active] By Default there is no scan of the observable. You can choose to add either active or passive scanning. See [doc](https://pulsedive.com/api/scan) for more info", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "basic", + "updated_at": "2024-02-09T10:52:19.020155Z", + "owner": None, + "analyzer_config": "Pulsedive", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0092_analyzer_config_phishtank"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0094_analyzer_config_qiling_linux.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0094_analyzer_config_qiling_linux.py new file mode 100644 index 0000000..4d9b4e4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0094_analyzer_config_qiling_linux.py @@ -0,0 +1,241 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Qiling_Linux", + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Qiling ELF emulation", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/x-sharedlib", + "application/x-executable", + "application/x-elf", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "linux", + "updated_at": "2024-02-09T10:52:19.151737Z", + "owner": None, + "analyzer_config": "Qiling_Linux", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "x86", + "updated_at": "2024-02-09T10:52:19.171039Z", + "owner": None, + "analyzer_config": "Qiling_Linux", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.184869Z", + "owner": None, + "analyzer_config": "Qiling_Linux", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.204697Z", + "owner": None, + "analyzer_config": "Qiling_Linux", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0093_analyzer_config_pulsedive"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0095_analyzer_config_qiling_linux_shellcode.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0095_analyzer_config_qiling_linux_shellcode.py new file mode 100644 index 0000000..f14ff17 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0095_analyzer_config_qiling_linux_shellcode.py @@ -0,0 +1,237 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Qiling_Linux_Shellcode", + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Qiling linux shellcode emulation", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/octet-stream"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "linux", + "updated_at": "2024-02-09T10:52:19.224216Z", + "owner": None, + "analyzer_config": "Qiling_Linux_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "x86", + "updated_at": "2024-02-09T10:52:19.259380Z", + "owner": None, + "analyzer_config": "Qiling_Linux_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.281956Z", + "owner": None, + "analyzer_config": "Qiling_Linux_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.300952Z", + "owner": None, + "analyzer_config": "Qiling_Linux_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0094_analyzer_config_qiling_linux"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0096_analyzer_config_qiling_windows.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0096_analyzer_config_qiling_windows.py new file mode 100644 index 0000000..a2a5a64 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0096_analyzer_config_qiling_windows.py @@ -0,0 +1,237 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Qiling_Windows", + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Qiling PE emulation", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "windows", + "updated_at": "2024-02-09T10:52:19.037004Z", + "owner": None, + "analyzer_config": "Qiling_Windows", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "x86", + "updated_at": "2024-02-09T10:52:19.048639Z", + "owner": None, + "analyzer_config": "Qiling_Windows", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.068140Z", + "owner": None, + "analyzer_config": "Qiling_Windows", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.089418Z", + "owner": None, + "analyzer_config": "Qiling_Windows", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0095_analyzer_config_qiling_linux_shellcode"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0097_analyzer_config_qiling_windows_shellcode.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0097_analyzer_config_qiling_windows_shellcode.py new file mode 100644 index 0000000..5c5393e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0097_analyzer_config_qiling_windows_shellcode.py @@ -0,0 +1,237 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Qiling_Windows_Shellcode", + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Qiling windows shellcode emulation", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/octet-stream"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "os", + "type": "str", + "description": "Change operating system for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "windows", + "updated_at": "2024-02-09T10:52:19.102289Z", + "owner": None, + "analyzer_config": "Qiling_Windows_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the emulation.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "x86", + "updated_at": "2024-02-09T10:52:19.114715Z", + "owner": None, + "analyzer_config": "Qiling_Windows_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "profile", + "type": "str", + "description": "Add a Qiling [profile](https://docs.qiling.io/en/latest/profile/).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.126550Z", + "owner": None, + "analyzer_config": "Qiling_Windows_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "qiling.Qiling", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.137624Z", + "owner": None, + "analyzer_config": "Qiling_Windows_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0096_analyzer_config_qiling_windows"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0098_analyzer_config_quad9_dns.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0098_analyzer_config_quad9_dns.py new file mode 100644 index 0000000..38dd9c7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0098_analyzer_config_quad9_dns.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Quad9_DNS", + "python_module": { + "module": "dns.dns_resolvers.quad9_dns_resolver.Quad9DNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Retrieve current domain resolution with Quad9 DoH (DNS over HTTPS)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "dns.dns_resolvers.quad9_dns_resolver.Quad9DNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "dns.dns_resolvers.quad9_dns_resolver.Quad9DNSResolver", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query_type", + "type": "str", + "description": "Query type against the chosen DNS resolver.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "A", + "updated_at": "2024-02-09T10:52:19.316550Z", + "owner": None, + "analyzer_config": "Quad9_DNS", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0097_analyzer_config_qiling_windows_shellcode"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0099_analyzer_config_quad9_malicious_detector.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0099_analyzer_config_quad9_malicious_detector.py new file mode 100644 index 0000000..f9fee6b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0099_analyzer_config_quad9_malicious_detector.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Quad9_Malicious_Detector", + "python_module": { + "module": "dns.dns_malicious_detectors.quad9_malicious_detector.Quad9MaliciousDetector", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check if a domain or an url is marked as malicious in Quad9 database", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0098_analyzer_config_quad9_dns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0100_analyzer_config_quark_engine.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0100_analyzer_config_quark_engine.py new file mode 100644 index 0000000..e8276a4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0100_analyzer_config_quark_engine.py @@ -0,0 +1,131 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Quark_Engine", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "2,5", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "0", + "hour": "0", + "day_of_week": "2,5", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "api_app.analyzers_manager.file_analyzers.quark_engine.QuarkEngineUpdate", + "task": "intel_owl.tasks.update", + "kwargs": '{"python_module_pk": 82}', + "queue": "long", + "enabled": True, + }, + "module": "quark_engine.QuarkEngine", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "An Obfuscation-Neglect Android Malware Scoring System", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/zip", + "application/java-archive", + "application/vnd.android.package-archive", + "application/x-dex", + "application/vnd.android.package-archive", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0099_analyzer_config_quad9_malicious_detector"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0101_analyzer_config_robtex.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0101_analyzer_config_robtex.py new file mode 100644 index 0000000..5c6c2ad --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0101_analyzer_config_robtex.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Robtex", + "python_module": { + "module": "robtex.Robtex", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan a domain/IP against the Robtex Passive DNS DB", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url", "ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0100_analyzer_config_quark_engine"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0102_analyzer_config_rtf_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0102_analyzer_config_rtf_info.py new file mode 100644 index 0000000..2c4df67 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0102_analyzer_config_rtf_info.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Rtf_Info", + "python_module": { + "module": "rtf_info.RTFInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "static RTF analysis (Oletools)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["text/rtf", "application/rtf"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0101_analyzer_config_robtex"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0103_analyzer_config_ssapinet.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0103_analyzer_config_ssapinet.py new file mode 100644 index 0000000..fd07e01 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0103_analyzer_config_ssapinet.py @@ -0,0 +1,253 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "SSAPINet", + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Get a screenshot of a web page using screenshotapi.net. For configuration, Refer to the [docs](https://screenshotapi.net/documentation).", + "disabled": False, + "soft_time_limit": 300, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "extra_api_params", + "type": "dict", + "description": "Refer to their docs](https://screenshotapi.net/documentation).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "proxy", + "type": "str", + "description": "Use the `use_proxy` and `proxy` options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "output", + "type": "str", + "description": "Specify output type (options: `image` i.e. raw base64 encoded image, `json` i.e. containing link)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "use_proxy", + "type": "bool", + "description": "Use the `use_proxy` and `proxy` options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "extra_api_params", + "type": "dict", + "description": "Refer to their docs](https://screenshotapi.net/documentation).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": { + "fresh": False, + "full_page": True, + "lazy_load": False, + "destroy_screenshot": False, + }, + "updated_at": "2024-02-09T10:52:19.568118Z", + "owner": None, + "analyzer_config": "SSAPINet", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "proxy", + "type": "str", + "description": "Use the `use_proxy` and `proxy` options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.516291Z", + "owner": None, + "analyzer_config": "SSAPINet", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "output", + "type": "str", + "description": "Specify output type (options: `image` i.e. raw base64 encoded image, `json` i.e. containing link)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "image", + "updated_at": "2024-02-09T10:52:19.538962Z", + "owner": None, + "analyzer_config": "SSAPINet", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "ss_api_net.SSAPINet", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "use_proxy", + "type": "bool", + "description": "Use the `use_proxy` and `proxy` options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.554907Z", + "owner": None, + "analyzer_config": "SSAPINet", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0102_analyzer_config_rtf_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0104_analyzer_config_securitytrails.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0104_analyzer_config_securitytrails.py new file mode 100644 index 0000000..3611216 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0104_analyzer_config_securitytrails.py @@ -0,0 +1,215 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Securitytrails", + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan a domain against [securitytrails API](https://securitytrails.com/)", + "disabled": True, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "securitytrails_analysis", + "type": "str", + "description": "Analysis Type. Available options: current, history", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "securitytrails_current_type", + "type": "str", + "description": "Suboptions if you chose 'current' analysis type. Options: details,subdomains,tags", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "securitytrails_history_type", + "type": "str", + "description": "Suboptions if you chose 'history' analysis type. Options: whois,dns", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "securitytrails_analysis", + "type": "str", + "description": "Analysis Type. Available options: current, history", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "current", + "updated_at": "2024-02-09T10:52:19.333335Z", + "owner": None, + "analyzer_config": "Securitytrails", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "securitytrails_current_type", + "type": "str", + "description": "Suboptions if you chose 'current' analysis type. Options: details,subdomains,tags", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "details", + "updated_at": "2024-02-09T10:52:19.345948Z", + "owner": None, + "analyzer_config": "Securitytrails", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "securitytrails.SecurityTrails", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "securitytrails_history_type", + "type": "str", + "description": "Suboptions if you chose 'history' analysis type. Options: whois,dns", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "whois", + "updated_at": "2024-02-09T10:52:19.358545Z", + "owner": None, + "analyzer_config": "Securitytrails", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0103_analyzer_config_ssapinet"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0105_analyzer_config_shodan_honeyscore.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0105_analyzer_config_shodan_honeyscore.py new file mode 100644 index 0000000..3e0c706 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0105_analyzer_config_shodan_honeyscore.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Shodan_Honeyscore", + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP against Shodan Honeyscore API", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "shodan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "shodan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "honeyscore", + "updated_at": "2024-02-09T10:52:19.378363Z", + "owner": None, + "analyzer_config": "Shodan_Honeyscore", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0104_analyzer_config_securitytrails"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0106_analyzer_config_shodan_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0106_analyzer_config_shodan_search.py new file mode 100644 index 0000000..de6b70b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0106_analyzer_config_shodan_search.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Shodan_Search", + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an IP against Shodan Search API", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "shodan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "shodan.Shodan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "shodan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "search", + "updated_at": "2024-02-09T10:52:19.396215Z", + "owner": None, + "analyzer_config": "Shodan_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0105_analyzer_config_shodan_honeyscore"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0107_analyzer_config_signature_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0107_analyzer_config_signature_info.py new file mode 100644 index 0000000..45c661e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0107_analyzer_config_signature_info.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Signature_Info", + "python_module": { + "module": "signature_info.SignatureInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "PE signature extractor with [osslsigncode](https://github.com/mtrojnar/osslsigncode)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0106_analyzer_config_shodan_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0108_analyzer_config_speakeasy.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0108_analyzer_config_speakeasy.py new file mode 100644 index 0000000..b435379 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0108_analyzer_config_speakeasy.py @@ -0,0 +1,204 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "SpeakEasy", + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "SpeakEasy emulation report by Mandiant", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the shellcode (x86 or x64).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "raw_offset", + "type": "int", + "description": "Offset to start emulating.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the shellcode (x86 or x64).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "x64", + "updated_at": "2024-02-09T10:52:19.419219Z", + "owner": None, + "analyzer_config": "SpeakEasy", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.431034Z", + "owner": None, + "analyzer_config": "SpeakEasy", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "raw_offset", + "type": "int", + "description": "Offset to start emulating.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 0, + "updated_at": "2024-02-09T10:52:19.443044Z", + "owner": None, + "analyzer_config": "SpeakEasy", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0107_analyzer_config_signature_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0109_analyzer_config_speakeasy_shellcode.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0109_analyzer_config_speakeasy_shellcode.py new file mode 100644 index 0000000..dac9c6f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0109_analyzer_config_speakeasy_shellcode.py @@ -0,0 +1,204 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "SpeakEasy_Shellcode", + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "SpeakEasy emulation shellcode report by Mandiant", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/octet-stream"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the shellcode (x86 or x64).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "raw_offset", + "type": "int", + "description": "Offset to start emulating.", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "arch", + "type": "str", + "description": "Change system architecture for the shellcode (x86 or x64).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "x64", + "updated_at": "2024-02-09T10:52:19.454089Z", + "owner": None, + "analyzer_config": "SpeakEasy_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "shellcode", + "type": "bool", + "description": "true if the file is a shellcode.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.465931Z", + "owner": None, + "analyzer_config": "SpeakEasy_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "speakeasy_emulation.SpeakEasy", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "raw_offset", + "type": "int", + "description": "Offset to start emulating.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 0, + "updated_at": "2024-02-09T10:52:19.479458Z", + "owner": None, + "analyzer_config": "SpeakEasy_Shellcode", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0108_analyzer_config_speakeasy"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0110_analyzer_config_spyse.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0110_analyzer_config_spyse.py new file mode 100644 index 0000000..e497643 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0110_analyzer_config_spyse.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Spyse", + "python_module": { + "module": "spyse.Spyse", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Scan domains, IPs, emails and CVEs using Spyse.com's API. Register [here](https://spyse.com/user/registration).", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "spyse.Spyse", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Spyse API token", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0109_analyzer_config_speakeasy_shellcode"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0111_analyzer_config_stalkphish.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0111_analyzer_config_stalkphish.py new file mode 100644 index 0000000..33c2b24 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0111_analyzer_config_stalkphish.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Stalkphish", + "python_module": { + "module": "stalkphish.Stalkphish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check in url or ipv4 if string exists in [Stalkphish.io](https://www.stalkphish.io/) API.", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["url", "ip", "domain", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "stalkphish.Stalkphish", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key.", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0110_analyzer_config_spyse"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0112_analyzer_config_stratosphere_blacklist.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0112_analyzer_config_stratosphere_blacklist.py new file mode 100644 index 0000000..0685fca --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0112_analyzer_config_stratosphere_blacklist.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Stratosphere_Blacklist", + "python_module": { + "module": "stratosphere.Stratos", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Cross reference an IP in blacklist provided by Stratosphere lab", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0111_analyzer_config_stalkphish"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0113_analyzer_config_strings_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0113_analyzer_config_strings_info.py new file mode 100644 index 0000000..6911daa --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0113_analyzer_config_strings_info.py @@ -0,0 +1,204 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Strings_Info", + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Strings extraction. Leverages Mandiant's [Stringsifter](https://github.com/mandiant/stringsifter)", + "disabled": False, + "soft_time_limit": 70, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "rank_strings", + "type": "bool", + "description": "enable ranking based on Machine Learning features", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_number_of_strings", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_characters_for_string", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "rank_strings", + "type": "bool", + "description": "enable ranking based on Machine Learning features", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.598271Z", + "owner": None, + "analyzer_config": "Strings_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_number_of_strings", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 500, + "updated_at": "2024-02-09T10:52:19.611443Z", + "owner": None, + "analyzer_config": "Strings_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "strings_info.StringsInfo", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_characters_for_string", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1000, + "updated_at": "2024-02-09T10:52:19.624174Z", + "owner": None, + "analyzer_config": "Strings_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0112_analyzer_config_stratosphere_blacklist"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0114_analyzer_config_sublimesecurity.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0114_analyzer_config_sublimesecurity.py new file mode 100644 index 0000000..5f28a3e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0114_analyzer_config_sublimesecurity.py @@ -0,0 +1,171 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "SublimeSecurity", + "python_module": { + "module": "sublime.Sublime", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Analyze email with sublime security", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": ["message/rfc822", "application/vnd.ms-outlook"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "sublime.Sublime", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "analyze_internal_eml_on_pec", + "type": "bool", + "description": "If True and a `.pec` email is sent, the internal email is analyzed as well", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "sublime.Sublime", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "url", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "sublime.Sublime", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "sublime.Sublime", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "message_source_id", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "sublime.Sublime", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "analyze_internal_eml_on_pec", + "type": "bool", + "description": "If True and a `.pec` email is sent, the internal email is analyzed as well", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.416948Z", + "owner": None, + "analyzer_config": "SublimeSecurity", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0113_analyzer_config_strings_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0115_analyzer_config_suricata.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0115_analyzer_config_suricata.py new file mode 100644 index 0000000..1771f23 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0115_analyzer_config_suricata.py @@ -0,0 +1,171 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Suricata", + "python_module": { + "module": "suricata.Suricata", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "PCAP analysis with Suricata rules", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/vnd.tcpdump.pcap"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "suricata.Suricata", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "reload_rules", + "type": "bool", + "description": "Set this to true to force the reload of Rules. Useful in case you just added new custom rules and want to test them. By default the Rules are updated only once a day at UTC 00:00", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "suricata.Suricata", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "extended_logs", + "type": "bool", + "description": "Set this to true to get all the raw logs generated by Suricata", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "suricata.Suricata", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "reload_rules", + "type": "bool", + "description": "Set this to true to force the reload of Rules. Useful in case you just added new custom rules and want to test them. By default the Rules are updated only once a day at UTC 00:00", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.637021Z", + "owner": None, + "analyzer_config": "Suricata", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "suricata.Suricata", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "extended_logs", + "type": "bool", + "description": "Set this to true to get all the raw logs generated by Suricata", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.649730Z", + "owner": None, + "analyzer_config": "Suricata", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0114_analyzer_config_sublimesecurity"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0116_analyzer_config_talosreputation.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0116_analyzer_config_talosreputation.py new file mode 100644 index 0000000..c34726b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0116_analyzer_config_talosreputation.py @@ -0,0 +1,125 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "TalosReputation", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "10", + "hour": "*/6", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "10", + "hour": "*/6", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "api_app.analyzers_manager.observable_analyzers.talos.TalosUpdate", + "task": "intel_owl.tasks.update", + "kwargs": '{"python_module_pk": 96}', + "queue": "default", + "enabled": True, + }, + "module": "talos.Talos", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check an IP reputation from Talos downloaded IP list", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0115_analyzer_config_suricata"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0117_analyzer_config_threatfox.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0117_analyzer_config_threatfox.py new file mode 100644 index 0000000..3802d29 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0117_analyzer_config_threatfox.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "ThreatFox", + "python_module": { + "module": "threatfox.ThreatFox", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "search for an IOC in ThreatFox's database", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0116_analyzer_config_talosreputation"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0118_analyzer_config_threatminer.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0118_analyzer_config_threatminer.py new file mode 100644 index 0000000..a347e2a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0118_analyzer_config_threatminer.py @@ -0,0 +1,138 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Threatminer", + "python_module": { + "module": "threatminer.Threatminer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "retrieve data from [Threatminer API](https://www.threatminer.org/api.php)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "threatminer.Threatminer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rt_value", + "type": "int", + "description": "Request type. Default is PassiveDNS(rt=2). See [ThreatMiner APIs](https://www.threatminer.org/api.php) for more details about how to leverage this option", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "threatminer.Threatminer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rt_value", + "type": "int", + "description": "Request type. Default is PassiveDNS(rt=2). See [ThreatMiner APIs](https://www.threatminer.org/api.php) for more details about how to leverage this option", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 2, + "updated_at": "2024-02-09T10:52:19.661481Z", + "owner": None, + "analyzer_config": "Threatminer", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0117_analyzer_config_threatfox"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0119_analyzer_config_thug_html_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0119_analyzer_config_thug_html_info.py new file mode 100644 index 0000000..8e9bbfc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0119_analyzer_config_thug_html_info.py @@ -0,0 +1,303 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Thug_HTML_Info", + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Perform hybrid dynamic/static analysis on a saved HTML page. For configuration, refer to [thug's usage docs](https://buffer.github.io/thug/doc/usage.html#basic-usage).", + "disabled": False, + "soft_time_limit": 600, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["text/html"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "proxy", + "type": "str", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "use_proxy", + "type": "bool", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "dom_events", + "type": "str", + "description": "See [Thug docs: dom events handling](https://buffer.github.io/thug/doc/usage.html#dom-events-handling).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": "See [Thug docs: browser personality](https://buffer.github.io/thug/doc/usage.html#browser-personality).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "enable_awis", + "type": "bool", + "description": "option `-E`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "enable_image_processing_analysis", + "type": "bool", + "description": "option `-a`", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "proxy", + "type": "str", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.673005Z", + "owner": None, + "analyzer_config": "Thug_HTML_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "use_proxy", + "type": "bool", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.685249Z", + "owner": None, + "analyzer_config": "Thug_HTML_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "dom_events", + "type": "str", + "description": "See [Thug docs: dom events handling](https://buffer.github.io/thug/doc/usage.html#dom-events-handling).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "click,mouseover", + "updated_at": "2024-02-09T10:52:19.697199Z", + "owner": None, + "analyzer_config": "Thug_HTML_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": "See [Thug docs: browser personality](https://buffer.github.io/thug/doc/usage.html#browser-personality).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "winxpie60", + "updated_at": "2024-02-09T10:52:19.709641Z", + "owner": None, + "analyzer_config": "Thug_HTML_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "enable_awis", + "type": "bool", + "description": "option `-E`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.722609Z", + "owner": None, + "analyzer_config": "Thug_HTML_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_file.ThugFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "enable_image_processing_analysis", + "type": "bool", + "description": "option `-a`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.735732Z", + "owner": None, + "analyzer_config": "Thug_HTML_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0118_analyzer_config_threatminer"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0120_analyzer_config_thug_url_info.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0120_analyzer_config_thug_url_info.py new file mode 100644 index 0000000..69694fb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0120_analyzer_config_thug_url_info.py @@ -0,0 +1,303 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Thug_URL_Info", + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Perform hybrid dynamic/static analysis on a URL", + "disabled": False, + "soft_time_limit": 600, + "routing_key": "local", + "health_check_status": True, + "type": "observable", + "docker_based": True, + "maximum_tlp": "RED", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "proxy", + "type": "str", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "use_proxy", + "type": "bool", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dom_events", + "type": "str", + "description": "See [Thug docs: dom events handling](https://buffer.github.io/thug/doc/usage.html#dom-events-handling).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": "See [Thug docs: browser personality](https://buffer.github.io/thug/doc/usage.html#browser-personality).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enable_awis", + "type": "bool", + "description": "option `-E`", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enable_image_processing_analysis", + "type": "bool", + "description": "option `-a`", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "proxy", + "type": "str", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:19.748697Z", + "owner": None, + "analyzer_config": "Thug_URL_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "use_proxy", + "type": "bool", + "description": "option `-p`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:19.762348Z", + "owner": None, + "analyzer_config": "Thug_URL_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dom_events", + "type": "str", + "description": "See [Thug docs: dom events handling](https://buffer.github.io/thug/doc/usage.html#dom-events-handling).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "click,mouseover", + "updated_at": "2024-02-09T10:52:19.777091Z", + "owner": None, + "analyzer_config": "Thug_URL_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": "See [Thug docs: browser personality](https://buffer.github.io/thug/doc/usage.html#browser-personality).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "winxpie60", + "updated_at": "2024-02-09T10:52:19.793319Z", + "owner": None, + "analyzer_config": "Thug_URL_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enable_awis", + "type": "bool", + "description": "option `-E`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.809742Z", + "owner": None, + "analyzer_config": "Thug_URL_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "thug_url.ThugUrl", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enable_image_processing_analysis", + "type": "bool", + "description": "option `-a`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:19.825585Z", + "owner": None, + "analyzer_config": "Thug_URL_Info", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0119_analyzer_config_thug_html_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0121_analyzer_config_torproject.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0121_analyzer_config_torproject.py new file mode 100644 index 0000000..6a49676 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0121_analyzer_config_torproject.py @@ -0,0 +1,125 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "TorProject", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "*/10", + "hour": "*", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "*/10", + "hour": "*", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "api_app.analyzers_manager.observable_analyzers.tor.TorUpdate", + "task": "intel_owl.tasks.update", + "kwargs": '{"python_module_pk": 101}', + "queue": "default", + "enabled": True, + }, + "module": "tor.Tor", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if an IP is a Tor Exit Node", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0120_analyzer_config_thug_url_info"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0122_analyzer_config_tranco.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0122_analyzer_config_tranco.py new file mode 100644 index 0000000..8a6ba56 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0122_analyzer_config_tranco.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Tranco", + "python_module": { + "module": "tranco.Tranco", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "check if a domain is in the last Tranco ranking top sites list", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0121_analyzer_config_torproject"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0123_analyzer_config_triage_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0123_analyzer_config_triage_scan.py new file mode 100644 index 0000000..9eb41cd --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0123_analyzer_config_triage_scan.py @@ -0,0 +1,215 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Triage_Scan", + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "leverage Triage sandbox environment to scan various files", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "endpoint", + "type": "str", + "description": "Choose whether to query on the public or the private endpoint of triage (options: `private`, `public`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "report_type", + "type": "str", + "description": "Determines how detailed the final report will be (options: `overview`, `complete`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "endpoint", + "type": "str", + "description": "Choose whether to query on the public or the private endpoint of triage (options: `private`, `public`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "public", + "updated_at": "2024-02-09T10:52:20.034882Z", + "owner": None, + "analyzer_config": "Triage_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 200, + "updated_at": "2024-02-09T10:52:20.050775Z", + "owner": None, + "analyzer_config": "Triage_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage_scan.TriageScanFile", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "report_type", + "type": "str", + "description": "Determines how detailed the final report will be (options: `overview`, `complete`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "overview", + "updated_at": "2024-02-09T10:52:20.066285Z", + "owner": None, + "analyzer_config": "Triage_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0122_analyzer_config_tranco"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0124_analyzer_config_triage_scan_url.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0124_analyzer_config_triage_scan_url.py new file mode 100644 index 0000000..d383678 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0124_analyzer_config_triage_scan_url.py @@ -0,0 +1,281 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Triage_Scan_URL", + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "analyze an URL using triage sandbox environment", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "long", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "GREEN", + "observable_supported": ["url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "endpoint", + "type": "str", + "description": "Choose whether to query on the public or the private endpoint of triage (options: `private`, `public`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "report_type", + "type": "str", + "description": "Determines how detailed the final report will be (options: `overview`, `complete`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "analysis_type", + "type": "str", + "description": "Choose whether to search for existing observable reports or upload for scanning via URL (options: `search` and `submit`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "Distance in seconds between each request", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "endpoint", + "type": "str", + "description": "Choose whether to query on the public or the private endpoint of triage (options: `private`, `public`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "public", + "updated_at": "2024-02-09T10:52:19.951179Z", + "owner": None, + "analyzer_config": "Triage_Scan_URL", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 200, + "updated_at": "2024-02-09T10:52:19.963984Z", + "owner": None, + "analyzer_config": "Triage_Scan_URL", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "report_type", + "type": "str", + "description": "Determines how detailed the final report will be (options: `overview`, `complete`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "overview", + "updated_at": "2024-02-09T10:52:19.977124Z", + "owner": None, + "analyzer_config": "Triage_Scan_URL", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "analysis_type", + "type": "str", + "description": "Choose whether to search for existing observable reports or upload for scanning via URL (options: `search` and `submit`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "submit", + "updated_at": "2024-02-09T10:52:19.991679Z", + "owner": None, + "analyzer_config": "Triage_Scan_URL", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "Distance in seconds between each request", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:20.008487Z", + "owner": None, + "analyzer_config": "Triage_Scan_URL", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0123_analyzer_config_triage_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0125_analyzer_config_triage_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0125_analyzer_config_triage_search.py new file mode 100644 index 0000000..4624423 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0125_analyzer_config_triage_search.py @@ -0,0 +1,281 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Triage_Search", + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "lookup a file hash or an URL in Triage public sandbox Database", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "endpoint", + "type": "str", + "description": "Choose whether to query on the public or the private endpoint of triage (options: `private`, `public`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "report_type", + "type": "str", + "description": "Determines how detailed the final report will be (options: `overview`, `complete`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "analysis_type", + "type": "str", + "description": "Choose whether to search for existing observable reports or upload for scanning via URL (options: `search` and `submit`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "Distance in seconds between each request", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "endpoint", + "type": "str", + "description": "Choose whether to query on the public or the private endpoint of triage (options: `private`, `public`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "public", + "updated_at": "2024-02-09T10:52:19.872882Z", + "owner": None, + "analyzer_config": "Triage_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 200, + "updated_at": "2024-02-09T10:52:19.889598Z", + "owner": None, + "analyzer_config": "Triage_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "report_type", + "type": "str", + "description": "Determines how detailed the final report will be (options: `overview`, `complete`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "overview", + "updated_at": "2024-02-09T10:52:19.906752Z", + "owner": None, + "analyzer_config": "Triage_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "analysis_type", + "type": "str", + "description": "Choose whether to search for existing observable reports or upload for scanning via URL (options: `search` and `submit`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "search", + "updated_at": "2024-02-09T10:52:19.920097Z", + "owner": None, + "analyzer_config": "Triage_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "triage.triage_search.TriageSearch", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "Distance in seconds between each request", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:19.932401Z", + "owner": None, + "analyzer_config": "Triage_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0124_analyzer_config_triage_scan_url"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0126_analyzer_config_urlhaus.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0126_analyzer_config_urlhaus.py new file mode 100644 index 0000000..44138fb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0126_analyzer_config_urlhaus.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "URLhaus", + "python_module": { + "module": "urlhaus.URLHaus", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Query a domain or URL against URLhaus API", + "disabled": False, + "soft_time_limit": 50, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["domain", "url", "ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0125_analyzer_config_triage_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0127_analyzer_config_unpacme.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0127_analyzer_config_unpacme.py new file mode 100644 index 0000000..4edb27f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0127_analyzer_config_unpacme.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "UnpacMe", + "python_module": { + "module": "unpac_me.UnpacMe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "UnpacMe unpacker", + "disabled": False, + "soft_time_limit": 400, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": ["application/x-dosexec"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "unpac_me.UnpacMe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "unpac_me.UnpacMe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "unpac_me.UnpacMe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "unpac_me.UnpacMe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private", + "type": "bool", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.696892Z", + "owner": None, + "analyzer_config": "UnpacMe", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "unpac_me.UnpacMe", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:21.742530Z", + "owner": None, + "analyzer_config": "UnpacMe", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0126_analyzer_config_urlhaus"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0128_analyzer_config_urlscan_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0128_analyzer_config_urlscan_search.py new file mode 100644 index 0000000..52783db --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0128_analyzer_config_urlscan_search.py @@ -0,0 +1,193 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "UrlScan_Search", + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Search an IP/domain/url/hash against [URLScan API](https://urlscan.io/docs/api/).", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "visibility", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_size", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "urlscan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key is mandatory for submit.", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_size", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 100, + "updated_at": "2024-02-09T10:52:20.135420Z", + "owner": None, + "analyzer_config": "UrlScan_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "urlscan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "search", + "updated_at": "2024-02-09T10:52:20.147002Z", + "owner": None, + "analyzer_config": "UrlScan_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0127_analyzer_config_unpacme"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0129_analyzer_config_urlscan_submit_result.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0129_analyzer_config_urlscan_submit_result.py new file mode 100644 index 0000000..36bafd7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0129_analyzer_config_urlscan_submit_result.py @@ -0,0 +1,215 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "UrlScan_Submit_Result", + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Submit & retrieve result of a URL against [URLScan API](https://urlscan.io/docs/api/).", + "disabled": False, + "soft_time_limit": 100, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "visibility", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_size", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "urlscan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key is mandatory for submit.", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "visibility", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "private", + "updated_at": "2024-02-09T10:52:20.090568Z", + "owner": None, + "analyzer_config": "UrlScan_Submit_Result", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_size", + "type": "int", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 100, + "updated_at": "2024-02-09T10:52:20.105272Z", + "owner": None, + "analyzer_config": "UrlScan_Submit_Result", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "urlscan.UrlScan", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "urlscan_analysis", + "type": "str", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "submit_result", + "updated_at": "2024-02-09T10:52:20.118798Z", + "owner": None, + "analyzer_config": "UrlScan_Submit_Result", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0128_analyzer_config_urlscan_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0130_analyzer_config_virustotal_v3_get_file.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0130_analyzer_config_virustotal_v3_get_file.py new file mode 100644 index 0000000..655f795 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0130_analyzer_config_virustotal_v3_get_file.py @@ -0,0 +1,479 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "VirusTotal_v3_Get_File", + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Check file hash on [VirusTotal](https://www.virustotal.com/). With TLP `CLEAR`, in case the hash is not found, you would send the file to the service.", + "disabled": False, + "soft_time_limit": 800, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "sha256", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "How many times we poll the VT API for scan results", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_max_tries", + "type": "int", + "description": "How many times we poll the VT API for RE-scan results (samples already available to VT)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs after having started a RE-scan", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_sigma_analyses", + "type": "bool", + "description": "Include sigma analysis report alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_elements", + "type": "int", + "description": "Number of elements to retrieve for each relationships", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "force_active_scan_if_old", + "type": "bool", + "description": "If the sample is old, it would be rescanned. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_to_request", + "type": "list", + "description": "Include a list of relationships to request if available. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_behaviour_summary", + "type": "bool", + "description": "Include a summary of behavioral analysis reports alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "days_to_say_that_a_scan_is_old", + "type": "int", + "description": "How many days are required to consider a scan old to force rescan", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_sub_path", + "type": "str", + "description": "if you want to query a specific subpath of the base endpoint, i.e: `analyses`", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "How many times we poll the VT API for scan results", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:21.000434Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:21.049051Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_max_tries", + "type": "int", + "description": "How many times we poll the VT API for RE-scan results (samples already available to VT)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 5, + "updated_at": "2024-02-09T10:52:21.104789Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs after having started a RE-scan", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 120, + "updated_at": "2024-02-09T10:52:21.132335Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_sigma_analyses", + "type": "bool", + "description": "Include sigma analysis report alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.171918Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_elements", + "type": "int", + "description": "Number of elements to retrieve for each relationships", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:21.213322Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "force_active_scan_if_old", + "type": "bool", + "description": "If the sample is old, it would be rescanned. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.252412Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_to_request", + "type": "list", + "description": "Include a list of relationships to request if available. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [], + "updated_at": "2024-02-09T10:52:21.291369Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_behaviour_summary", + "type": "bool", + "description": "Include a summary of behavioral analysis reports alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.334925Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "days_to_say_that_a_scan_is_old", + "type": "int", + "description": "How many days are required to consider a scan old to force rescan", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:21.352130Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_sub_path", + "type": "str", + "description": "if you want to query a specific subpath of the base endpoint, i.e: `analyses`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:21.019911Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_File", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0129_analyzer_config_urlscan_submit_result"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0131_analyzer_config_virustotal_v3_get_observable.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0131_analyzer_config_virustotal_v3_get_observable.py new file mode 100644 index 0000000..7d58ace --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0131_analyzer_config_virustotal_v3_get_observable.py @@ -0,0 +1,479 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "VirusTotal_v3_Get_Observable", + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "search an observable in the VirusTotal DB", + "disabled": False, + "soft_time_limit": 800, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "How many times we poll the VT API for scan results", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_max_tries", + "type": "int", + "description": "How many times we poll the VT API for RE-scan results (samples already available to VT)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs after having started a RE-scan", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_sigma_analyses", + "type": "bool", + "description": "Include sigma analysis report alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_elements", + "type": "int", + "description": "Number of elements to retrieve for each relationships", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "force_active_scan_if_old", + "type": "bool", + "description": "If the sample is old, it would be rescanned. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_to_request", + "type": "list", + "description": "Include a list of relationships to request if available. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_behaviour_summary", + "type": "bool", + "description": "Include a summary of behavioral analysis reports alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "days_to_say_that_a_scan_is_old", + "type": "int", + "description": "How many days are required to consider a scan old to force rescan", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_sub_path", + "type": "str", + "description": "if you want to query a specific subpath of the base endpoint, i.e: `analyses`", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "max_tries", + "type": "int", + "description": "How many times we poll the VT API for scan results", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 10, + "updated_at": "2024-02-09T10:52:21.375283Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:21.399856Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_max_tries", + "type": "int", + "description": "How many times we poll the VT API for RE-scan results (samples already available to VT)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 5, + "updated_at": "2024-02-09T10:52:21.411674Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "rescan_poll_distance", + "type": "int", + "description": "IntelOwl would sleep for this time between each poll to VT APIs after having started a RE-scan", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 120, + "updated_at": "2024-02-09T10:52:21.424006Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_sigma_analyses", + "type": "bool", + "description": "Include sigma analysis report alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.436444Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_elements", + "type": "int", + "description": "Number of elements to retrieve for each relationships", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:21.454125Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "force_active_scan_if_old", + "type": "bool", + "description": "If the sample is old, it would be rescanned. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.492808Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "relationships_to_request", + "type": "list", + "description": "Include a list of relationships to request if available. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [], + "updated_at": "2024-02-09T10:52:21.527682Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "include_behaviour_summary", + "type": "bool", + "description": "Include a summary of behavioral analysis reports alongside default scan report. This will cost additional quota.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:21.569911Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "days_to_say_that_a_scan_is_old", + "type": "int", + "description": "How many days are required to consider a scan old to force rescan", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 30, + "updated_at": "2024-02-09T10:52:21.612115Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_get.VirusTotalv3", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_sub_path", + "type": "str", + "description": "if you want to query a specific subpath of the base endpoint, i.e: `analyses`", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:21.387972Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Get_Observable", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0130_analyzer_config_virustotal_v3_get_file"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0132_analyzer_config_virustotal_v3_intelligence_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0132_analyzer_config_virustotal_v3_intelligence_search.py new file mode 100644 index 0000000..9831912 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0132_analyzer_config_virustotal_v3_intelligence_search.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "VirusTotal_v3_Intelligence_Search", + "python_module": { + "module": "vt.vt3_intelligence_search.VirusTotalv3Intelligence", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "perform an advanced query through VirusTotal Intelligence. This is a premium feature only", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "vt.vt3_intelligence_search.VirusTotalv3Intelligence", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "max number of results to retrieve", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_intelligence_search.VirusTotalv3Intelligence", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "order_by", + "type": "str", + "description": "More information in VT [docs](https://developers.virustotal.com/reference/intelligence-search) ", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vt.vt3_intelligence_search.VirusTotalv3Intelligence", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API Key for the analyzer. You need to have a premium license with VT.", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "vt.vt3_intelligence_search.VirusTotalv3Intelligence", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "limit", + "type": "int", + "description": "max number of results to retrieve", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 300, + "updated_at": "2024-02-09T10:52:20.510902Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Intelligence_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "vt.vt3_intelligence_search.VirusTotalv3Intelligence", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "order_by", + "type": "str", + "description": "More information in VT [docs](https://developers.virustotal.com/reference/intelligence-search) ", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:20.522599Z", + "owner": None, + "analyzer_config": "VirusTotal_v3_Intelligence_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0131_analyzer_config_virustotal_v3_get_observable"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0133_analyzer_config_virushee_checkhash.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0133_analyzer_config_virushee_checkhash.py new file mode 100644 index 0000000..81ec0c2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0133_analyzer_config_virushee_checkhash.py @@ -0,0 +1,118 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Virushee_CheckHash", + "python_module": { + "module": "virushee.VirusheeCheckHash", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Search for a previous analysis of a file by its hash (SHA256/SHA1/MD5) on [Virushee API](https://api.virushee.com/).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "virushee.VirusheeCheckHash", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "[virushee docs](https://api.virushee.com/#section/Authentication)", + "is_secret": True, + "required": False, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "analyzers_manager", + "0002_0132_analyzer_config_virustotal_v3_intelligence_search", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0134_analyzer_config_virushee_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0134_analyzer_config_virushee_scan.py new file mode 100644 index 0000000..b187939 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0134_analyzer_config_virushee_scan.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Virushee_Scan", + "python_module": { + "module": "virushee.VirusheeFileUpload", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": 'Check file hash for analysis on [Virushee API]("https://api.virushee.com/). With TLP `CLEAR`, in case the hash is not found, you would send the file to the service.', + "disabled": False, + "soft_time_limit": 300, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "virushee.VirusheeFileUpload", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "[virushee docs](https://api.virushee.com/#section/Authentication)", + "is_secret": True, + "required": False, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0133_analyzer_config_virushee_checkhash"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0135_analyzer_config_whois_ripedb_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0135_analyzer_config_whois_ripedb_search.py new file mode 100644 index 0000000..52a6832 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0135_analyzer_config_whois_ripedb_search.py @@ -0,0 +1,103 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "WhoIs_RipeDB_Search", + "python_module": { + "module": "whoisripe.WhoIsRipeAPI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Fetch whois record data of an IP address from Ripe DB using their [search API](https://github.com/RIPE-NCC/whois/wiki/WHOIS-REST-API-search)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0134_analyzer_config_virushee_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0136_analyzer_config_whoisxmlapi.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0136_analyzer_config_whoisxmlapi.py new file mode 100644 index 0000000..28d249c --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0136_analyzer_config_whoisxmlapi.py @@ -0,0 +1,115 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Whoisxmlapi", + "python_module": { + "module": "whoisxmlapi.Whoisxmlapi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "the WHOIS record data, of a domain name, an IP address, or an email address", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "whoisxmlapi.Whoisxmlapi", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0135_analyzer_config_whois_ripedb_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0137_analyzer_config_wigle.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0137_analyzer_config_wigle.py new file mode 100644 index 0000000..0d69f88 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0137_analyzer_config_wigle.py @@ -0,0 +1,149 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "WiGLE", + "python_module": { + "module": "wigle.WiGLE", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Maps and database of 802.11 wireless networks, with statistics, submitted by wardrivers, netstumblers, and net huggers. Also, the string to be passed in input field of generic analyzers have a format. Different variables are separated by semicolons(`;`) and the field-name and value are separated by equals sign(`=`). Example string for search_type `CDMA Network` is `sid=12345;nid=12345;bsid=12345`.", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "wigle.WiGLE", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_type", + "type": "str", + "description": "Corresponds to different routes mentioned in [docs](https://api.wigle.net/swagger#/v3_ALPHA). (options: `WiFi Network`, `CDMA Network`, `Bluetooth Network`, `GSM/LTE/WCDMA Network`).", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "wigle.WiGLE", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "wigle.WiGLE", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_type", + "type": "str", + "description": "Corresponds to different routes mentioned in [docs](https://api.wigle.net/swagger#/v3_ALPHA). (options: `WiFi Network`, `CDMA Network`, `Bluetooth Network`, `GSM/LTE/WCDMA Network`).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "WiFi Network", + "updated_at": "2024-02-09T10:52:20.547236Z", + "owner": None, + "analyzer_config": "WiGLE", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0136_analyzer_config_whoisxmlapi"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0138_analyzer_config_xforceexchange.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0138_analyzer_config_xforceexchange.py new file mode 100644 index 0000000..00e7e53 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0138_analyzer_config_xforceexchange.py @@ -0,0 +1,193 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "XForceExchange", + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable on IBM X-Force Exchange", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "hash", "domain", "url"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Request timeout", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "malware_only", + "type": "bool", + "description": "Performs lookup only against 'malware' endpoints to save some quota", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_password_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "Request timeout", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 5, + "updated_at": "2024-02-09T10:52:20.473414Z", + "owner": None, + "analyzer_config": "XForceExchange", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "xforce.XForce", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "malware_only", + "type": "bool", + "description": "Performs lookup only against 'malware' endpoints to save some quota", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:20.486232Z", + "owner": None, + "analyzer_config": "XForceExchange", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0137_analyzer_config_wigle"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0139_analyzer_config_xlm_macro_deobfuscator.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0139_analyzer_config_xlm_macro_deobfuscator.py new file mode 100644 index 0000000..aaf8c76 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0139_analyzer_config_xlm_macro_deobfuscator.py @@ -0,0 +1,148 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Xlm_Macro_Deobfuscator", + "python_module": { + "module": "xlm_macro_deobfuscator.XlmMacroDeobfuscator", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Extracting Office XLM Macros with [XlmMacroDeobfuscator](https://github.com/DissectMalware/XLMMacroDeobfuscator)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/vnd.ms-excel.addin.macroEnabled", + "application/x-mspublisher", + "application/vnd.ms-excel", + "application/vnd.ms-excel.sheet.macroEnabled.12", + "application/excel", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "application/zip", + "application/xml", + "application/encrypted", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "xlm_macro_deobfuscator.XlmMacroDeobfuscator", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "passwords_to_check", + "type": "list", + "description": "", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "xlm_macro_deobfuscator.XlmMacroDeobfuscator", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "passwords_to_check", + "type": "list", + "description": "", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": ["agenzia", "inps", "coronavirus"], + "updated_at": "2024-02-09T10:52:20.565284Z", + "owner": None, + "analyzer_config": "Xlm_Macro_Deobfuscator", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0138_analyzer_config_xforceexchange"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0140_analyzer_config_yaraify_file_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0140_analyzer_config_yaraify_file_scan.py new file mode 100644 index 0000000..272ff17 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0140_analyzer_config_yaraify_file_scan.py @@ -0,0 +1,358 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "YARAify_File_Scan", + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "Scan a file against public and non-public YARA and ClamAV signatures in [YARAify service](https://yaraify.abuse.ch/). With TLP `CLEAR`, in case the hash is not found, you would send the file to the service.", + "disabled": False, + "soft_time_limit": 500, + "routing_key": "long", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "unpack", + "type": "bool", + "description": "Defines whether to unpack the file.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "send_file", + "type": "bool", + "description": "Defines whether the file should be sent for analysis or not (in the latter case hash only check would be done)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "share_file", + "type": "bool", + "description": "Defines whether the file is public and may be shared with 3rd parties.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "skip_known", + "type": "bool", + "description": "YARAify will not process the file if the file is already known.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "skip_noisy", + "type": "bool", + "description": "YARAify skips the file if it already has been scanned at least 10 times within the past 24 hours. It will return the latest task_id instead", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "clamav_scan", + "type": "bool", + "description": "Defines whether to scan the file with ClamAV.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_identifier", + "type": "str", + "description": "Optional identifier to associate this submission with", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Optional key to receive results from public (TLP:CLEAR) and non-public (TLP:GREEN, TLP:AMBER and TLP:RED) YARA rules.", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "unpack", + "type": "bool", + "description": "Defines whether to unpack the file.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:20.624667Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "send_file", + "type": "bool", + "description": "Defines whether the file should be sent for analysis or not (in the latter case hash only check would be done)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:20.637754Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 25, + "updated_at": "2024-02-09T10:52:20.651957Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "share_file", + "type": "bool", + "description": "Defines whether the file is public and may be shared with 3rd parties.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:20.663677Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "skip_known", + "type": "bool", + "description": "YARAify will not process the file if the file is already known.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:20.677107Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "skip_noisy", + "type": "bool", + "description": "YARAify skips the file if it already has been scanned at least 10 times within the past 24 hours. It will return the latest task_id instead", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:20.691102Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify_file_scan.YARAifyFileScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "clamav_scan", + "type": "bool", + "description": "Defines whether to scan the file with ClamAV.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:20.701978Z", + "owner": None, + "analyzer_config": "YARAify_File_Scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0139_analyzer_config_xlm_macro_deobfuscator"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0141_analyzer_config_yaraify_file_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0141_analyzer_config_yaraify_file_search.py new file mode 100644 index 0000000..c33bee9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0141_analyzer_config_yaraify_file_search.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "YARAify_File_Search", + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "lookup a file hash in YARAify", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": True, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "using: 'get_yara' for YARA rule (default), 'get_clamav' for ClamAV signature, 'get_imphash' for imphash, 'get_tlsh' for TLSH, 'get_telfhash' for telfhash or 'get_dhash_icon' for icon_dash", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Optional key to receive results from public (TLP:CLEAR) and also non-public (TLP:GREEN, TLP:AMBER and TLP:RED) YARA rules.", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "using: 'get_yara' for YARA rule (default), 'get_clamav' for ClamAV signature, 'get_imphash' for imphash, 'get_tlsh' for TLSH, 'get_telfhash' for telfhash or 'get_dhash_icon' for icon_dash", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "lookup_hash", + "updated_at": "2024-02-09T10:52:20.756954Z", + "owner": None, + "analyzer_config": "YARAify_File_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 25, + "updated_at": "2024-02-09T10:52:20.778692Z", + "owner": None, + "analyzer_config": "YARAify_File_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0140_analyzer_config_yaraify_file_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0142_analyzer_config_yaraify_generics.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0142_analyzer_config_yaraify_generics.py new file mode 100644 index 0000000..5169dff --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0142_analyzer_config_yaraify_generics.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "YARAify_Generics", + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "lookup a YARA rule (default), ClamAV rule, imphash, TLSH, telfhash or icon_dash in YARAify", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "using: 'get_yara' for YARA rule (default), 'get_clamav' for ClamAV signature, 'get_imphash' for imphash, 'get_tlsh' for TLSH, 'get_telfhash' for telfhash or 'get_dhash_icon' for icon_dash", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Optional key to receive results from public (TLP:CLEAR) and also non-public (TLP:GREEN, TLP:AMBER and TLP:RED) YARA rules.", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "using: 'get_yara' for YARA rule (default), 'get_clamav' for ClamAV signature, 'get_imphash' for imphash, 'get_tlsh' for TLSH, 'get_telfhash' for telfhash or 'get_dhash_icon' for icon_dash", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "get_yara", + "updated_at": "2024-02-09T10:52:20.728181Z", + "owner": None, + "analyzer_config": "YARAify_Generics", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 25, + "updated_at": "2024-02-09T10:52:20.741755Z", + "owner": None, + "analyzer_config": "YARAify_Generics", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0141_analyzer_config_yaraify_file_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0143_analyzer_config_yaraify_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0143_analyzer_config_yaraify_search.py new file mode 100644 index 0000000..50eddae --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0143_analyzer_config_yaraify_search.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "YARAify_Search", + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "lookup a file hash in YARAify", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "using: 'get_yara' for YARA rule (default), 'get_clamav' for ClamAV signature, 'get_imphash' for imphash, 'get_tlsh' for TLSH, 'get_telfhash' for telfhash or 'get_dhash_icon' for icon_dash", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Optional key to receive results from public (TLP:CLEAR) and also non-public (TLP:GREEN, TLP:AMBER and TLP:RED) YARA rules.", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "using: 'get_yara' for YARA rule (default), 'get_clamav' for ClamAV signature, 'get_imphash' for imphash, 'get_tlsh' for TLSH, 'get_telfhash' for telfhash or 'get_dhash_icon' for icon_dash", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "lookup_hash", + "updated_at": "2024-02-09T10:52:20.801173Z", + "owner": None, + "analyzer_config": "YARAify_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yaraify.YARAify", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "result_max", + "type": "int", + "description": "Max number of results you want to display (default: 25, max: 1'000)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 25, + "updated_at": "2024-02-09T10:52:20.817148Z", + "owner": None, + "analyzer_config": "YARAify_Search", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0142_analyzer_config_yaraify_generics"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0144_analyzer_config_yeti.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0144_analyzer_config_yeti.py new file mode 100644 index 0000000..54a5cb1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0144_analyzer_config_yeti.py @@ -0,0 +1,226 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "YETI", + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "scan an observable on a custom YETI instance", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "url", "hash", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "regex", + "type": "bool", + "description": "Use this if you are searching for observables using a regex.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verify_ssl", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your YETI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "results_count", + "type": "int", + "description": "Use this to limit the maximum number of results obtained from a search.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for your YETI instance", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_key_name", + "type": "str", + "description": "API URL of your YETI instance", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "regex", + "type": "bool", + "description": "Use this if you are searching for observables using a regex.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:20.844799Z", + "owner": None, + "analyzer_config": "YETI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "verify_ssl", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your YETI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:20.861061Z", + "owner": None, + "analyzer_config": "YETI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "results_count", + "type": "int", + "description": "Use this to limit the maximum number of results obtained from a search.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 50, + "updated_at": "2024-02-09T10:52:20.876848Z", + "owner": None, + "analyzer_config": "YETI", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0143_analyzer_config_yaraify_search"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0145_analyzer_config_yara.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0145_analyzer_config_yara.py new file mode 100644 index 0000000..850febf --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0145_analyzer_config_yara.py @@ -0,0 +1,263 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Yara", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "api_app.analyzers_manager.file_analyzers.yara_scan.YaraScanUpdate", + "task": "intel_owl.tasks.update", + "kwargs": '{"python_module_pk": 122}', + "queue": "local", + "enabled": True, + }, + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "description": "scan a file with Yara rules", + "disabled": False, + "soft_time_limit": 120, + "routing_key": "local", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": ["application/vnd.tcpdump.pcap"], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "ignore", + "type": "list", + "description": "ignore these rules", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "local_rules", + "type": "bool", + "description": "If True, use local rules present at /opt/deploy/files_required/yara/YOUR_USER/custom_rule", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "repositories", + "type": "list", + "description": "Repositories that will be constantly updated", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "private_repositories", + "type": "dict", + "description": 'Private repositories in the following format: {"username@provider:org/repository.git":"ssh key"}. Use double quote, don\'t worry about whitespace.', + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "ignore", + "type": "list", + "description": "ignore these rules", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [ + "generic_anomalies.yar", + "general_cloaking.yar", + "thor_inverse_matches.yar", + "yara_mixed_ext_vars.yar", + "thor-webshells.yar", + ], + "updated_at": "2024-02-09T10:52:20.577189Z", + "owner": None, + "analyzer_config": "Yara", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "local_rules", + "type": "bool", + "description": "If True, use local rules present at /opt/deploy/files_required/yara/YOUR_USER/custom_rule", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:20.589012Z", + "owner": None, + "analyzer_config": "Yara", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "yara_scan.YaraScan", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "repositories", + "type": "list", + "description": "Repositories that will be constantly updated", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": [ + "https://github.com/dr4k0nia/yara-rules", + "https://github.com/elastic/protections-artifacts", + "https://github.com/embee-research/Yara", + "https://github.com/elceef/yara-rulz", + "https://github.com/JPCERTCC/jpcert-yara", + "https://github.com/SIFalcon/Detection/", + "https://github.com/bartblaze/Yara-rules", + "https://github.com/intezer/yara-rules", + "https://github.com/advanced-threat-research/Yara-Rules", + "https://github.com/stratosphereips/yara-rules", + "https://github.com/reversinglabs/reversinglabs-yara-rules", + "https://github.com/sbousseaden/YaraHunts", + "https://github.com/InQuest/yara-rules", + "https://github.com/StrangerealIntel/DailyIOC", + "https://github.com/mandiant/red_team_tool_countermeasures", + "https://github.com/fboldewin/YARA-rules", + "https://github.com/Yara-Rules/rules.git", + "https://github.com/Neo23x0/signature-base.git", + "https://yaraify-api.abuse.ch/yarahub/yaraify-rules.zip", + ], + "updated_at": "2024-02-09T10:52:28.002269Z", + "owner": None, + "analyzer_config": "Yara", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0144_analyzer_config_yeti"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0146_analyzer_config_zoomeye.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0146_analyzer_config_zoomeye.py new file mode 100644 index 0000000..e1ae4fb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0002_0146_analyzer_config_zoomeye.py @@ -0,0 +1,281 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "ZoomEye", + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "description": "Cyberspace Search Engine recording information of devices, websites, services, components etc. [Host search: docs](https://www.zoomeye.org/doc#host-search).", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page", + "type": "int", + "description": "Page number to fetch.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "Follow according to docs, but omit `ip`, `hostname`. Eg: `city:beijing port:21`.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "facets", + "type": "str", + "description": "A comma-separated list of properties to get summary information on query. Eg: `facets:app,os`.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "history", + "type": "bool", + "description": "To query the history data.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_type", + "type": "str", + "description": "Choose among `host`, `web`, `both` (both is only available to ZoomEye VIP users).", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page", + "type": "int", + "description": "Page number to fetch.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:20.909659Z", + "owner": None, + "analyzer_config": "ZoomEye", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "query", + "type": "str", + "description": "Follow according to docs, but omit `ip`, `hostname`. Eg: `city:beijing port:21`.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:20.925799Z", + "owner": None, + "analyzer_config": "ZoomEye", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "facets", + "type": "str", + "description": "A comma-separated list of properties to get summary information on query. Eg: `facets:app,os`.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:20.942503Z", + "owner": None, + "analyzer_config": "ZoomEye", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "history", + "type": "bool", + "description": "To query the history data.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:20.959843Z", + "owner": None, + "analyzer_config": "ZoomEye", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "zoomeye.ZoomEye", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "search_type", + "type": "str", + "description": "Choose among `host`, `web`, `both` (both is only available to ZoomEye VIP users).", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "host", + "updated_at": "2024-02-09T10:52:20.976834Z", + "owner": None, + "analyzer_config": "ZoomEye", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0145_analyzer_config_yara"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_1_change_primary_key.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_1_change_primary_key.py new file mode 100644 index 0000000..ccf91df --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_1_change_primary_key.py @@ -0,0 +1,12 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0002_0146_analyzer_config_zoomeye"), + ("playbooks_manager", "0001_initial_squashed"), + ("api_app", "0001_2_initial_squashed"), + ] + + operations = [] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_2_change_primary_key.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_2_change_primary_key.py new file mode 100644 index 0000000..77af462 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_2_change_primary_key.py @@ -0,0 +1,43 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + AnalyzerConfig.objects.update( + disabled2=ArraySubquery( + AnalyzerConfig.objects.filter(pk=models.OuterRef("pk")).values( + "disabled_in_organizations__pk" + ) + ) + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_1_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="analyzerconfig", + name="disabled2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.IntegerField(), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate), + migrations.AlterField( + model_name="analyzerreport", + name="config", + field=models.CharField(max_length=100, null=False, blank=False), + ), + migrations.RemoveField( + model_name="analyzerconfig", name="disabled_in_organizations" + ), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_3_change_primary_key.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_3_change_primary_key.py new file mode 100644 index 0000000..615c16f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_3_change_primary_key.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_2_change_primary_key"), + ("api_app", "0057_2_change_primary_key"), + ("playbooks_manager", "0023_2_change_primary_key"), + ("pivots_manager", "0023_2_change_primary_key"), + ] + + operations = [ + migrations.RunSQL( + 'ALTER TABLE "analyzers_manager_analyzerconfig" DROP CONSTRAINT "analyzers_manager_analyzerconfig_pkey" CASCADE;' + ), + migrations.AlterField( + model_name="analyzerconfig", + name="name", + field=models.CharField( + max_length=100, + unique=True, + primary_key=False, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", "Your name should match the [A-Za-z0-9_] characters" + ) + ], + ), + ), + migrations.AddField( + model_name="analyzerconfig", + name="id", + field=models.BigAutoField( + auto_created=True, serialize=False, verbose_name="ID", primary_key=True + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_4_change_primary_key.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_4_change_primary_key.py new file mode 100644 index 0000000..0e76bb1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0058_4_change_primary_key.py @@ -0,0 +1,69 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + AnalyzerReport = apps.get_model("analyzers_manager", "AnalyzerReport") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + Organization = apps.get_model("certego_saas_organization", "Organization") + name = AnalyzerConfig.objects.filter( + name=models.OuterRef("old_config") + ).values_list("pk")[:1] + AnalyzerReport.objects.update(config=models.Subquery(name)) + for config in AnalyzerConfig.objects.all(): + if config.disabled2: + ContentType = apps.get_model("contenttypes", "ContentType") + ct = ContentType.objects.get_for_model(config) + OrganizationPluginConfiguration = apps.get_model( + "api_app", "OrganizationPluginConfiguration" + ) + for org in config.disabled2: + if org: + OrganizationPluginConfiguration.objects.create( + organization=Organization.objects.get(pk=org), + object_id=config.pk, + content_type=ct, + disabled=True, + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("analyzers_manager", "0058_3_change_primary_key"), + ] + + operations = [ + migrations.RenameField( + model_name="analyzerreport", old_name="config", new_name="old_config" + ), + migrations.AddField( + model_name="analyzerreport", + name="config", + field=models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + null=True, + to="analyzers_manager.analyzerconfig", + ), + preserve_default=False, + ), + migrations.RunPython(migrate), + migrations.AlterField( + model_name="analyzerreport", + name="config", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="analyzers_manager.analyzerconfig", + ), + ), + migrations.AlterUniqueTogether( + name="analyzerreport", + unique_together={("config", "job")}, + ), + migrations.RemoveField(model_name="analyzerconfig", name="disabled2"), + migrations.RemoveField(model_name="analyzerreport", name="old_config"), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0059_alter_analyzer_config_dns0_rrsets_data.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0059_alter_analyzer_config_dns0_rrsets_data.py new file mode 100644 index 0000000..ddcf337 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0059_alter_analyzer_config_dns0_rrsets_data.py @@ -0,0 +1,36 @@ +from django.db import migrations + +from api_app.analyzers_manager.constants import ObservableTypes + + +def migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + config = AnalyzerConfig.objects.get(name="DNS0_rrsets_data") + config.observable_supported = [ + ObservableTypes.DOMAIN, + ObservableTypes.GENERIC, + ObservableTypes.IP, + ] + config.full_clean() + config.save() + + +def reverse_migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + config = AnalyzerConfig.objects.get(name="DNS0_rrsets_data") + config.observable_supported = [ + ObservableTypes.DOMAIN, + ObservableTypes.URL, + ObservableTypes.GENERIC, + ObservableTypes.IP, + ] + config.full_clean() + config.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_4_change_primary_key"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0060_analyzer_config_ip2location.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0060_analyzer_config_ip2location.py new file mode 100644 index 0000000..b5641b8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0060_analyzer_config_ip2location.py @@ -0,0 +1,122 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "module": "ip2location.Ip2location", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Ip2location", + "description": "[API Docs](https://www.ip2location.io/ip2location-documentation) IP2Location.io allows users to check IP address location in real time. (Supports both with or without key)", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ip2location.Ip2location", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "Ip2location API key name.", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "ip2location.Ip2location", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_version", + "type": "str", + "description": "Ip2location API version keyed or keyless.", + "is_secret": False, + "required": True, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + _create_object(Model, plugin) + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ("analyzers_manager", "0059_alter_analyzer_config_dns0_rrsets_data"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0061_analyzer_config_ipqs_fraud_and_risk_scoring.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0061_analyzer_config_ipqs_fraud_and_risk_scoring.py new file mode 100644 index 0000000..1cfd2de --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0061_analyzer_config_ipqs_fraud_and_risk_scoring.py @@ -0,0 +1,331 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "IPQS_Fraud_And_Risk_Scoring", + "description": "Scan an Observable against IPQualityscore.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "url", "domain", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "user_language", + "type": "str", + "description": 'You can optionally provide us with the user\'s language header. This allows us to evaluate the risk of the user as judged in the "fraud_score".', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "user_agent", + "type": "str", + "description": 'You can optionally provide us with the user agent string (browser). This allows us to run additional checks to see if the user is a bot or running an invalid browser. This allows us to evaluate the risk of the user as judged in the "fraud_score".', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "transaction_strictness", + "type": "int", + "description": "Adjusts the weights for penalties applied due to irregularities and fraudulent patterns detected on order and transaction details that can be optionally provided on each API request. This feature is only beneficial if you are passing order and transaction details. A table is available further down the page with supported transaction variables.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ip_strictness", + "type": "int", + "description": 'How in depth (strict) do you want this query to be? Higher values take longer to process and may provide a higher false-positive rate. We recommend starting at "0", the lowest strictness setting, and increasing to "1" depending on your levels of fraud. Levels 2+ are VERY strict and will produce false-positives.', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "mobile", + "type": "bool", + "description": "You can optionally specify that this lookup should be treated as a mobile device. Recommended for mobile lookups that do not have a user agent attached to the request. NOTE: This can cause unexpected and abnormal results if the device is not a mobile device.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "lighter_penalties", + "type": "bool", + "description": "Is your scoring too strict? Enable this setting to lower detection rates and Fraud Scores for mixed quality IP addresses. If you experience any false-positives with your traffic then enabling this feature will provide better results.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ip_fast", + "type": "bool", + "description": "When this parameter is enabled our API will not perform certain forensic checks that take longer to process. Enabling this feature greatly increases the API speed without much impact on accuracy. This option is intended for services that require decision making in a time sensitive manner and can be used for any strictness level.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "allow_public_access_points", + "type": "bool", + "description": "Bypasses certain checks for IP addresses from education and research institutions, schools, and some corporate connections to better accommodate audiences that frequently use public connections.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "email_timeout", + "type": "int", + "description": 'Maximum number of seconds to wait for a reply from a mail service provider. If your implementation requirements do not need an immediate response, we recommend bumping this value to 20. Any results which experience a connection timeout will return the "timed_out" variable as true. Default value is 7 seconds.', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "suggest_domain", + "type": "bool", + "description": 'Force analyze if the email address\'s domain has a typo and should be corrected to a popular mail service. By default, this test is currently only performed when the email is invalid or if the "recent abuse" status is true.', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "email_strictness", + "type": "int", + "description": "Sets how strictly spam traps and honeypots are detected by our system, depending on how comfortable you are with identifying emails suspected of being a spam trap. 0 is the lowest level which will only return spam traps with high confidence. Strictness levels above 0 will return increasingly more strict results, with level 2 providing the greatest detection rates.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "email_fast", + "type": "bool", + "description": "When this parameter is enabled our API will not perform an SMTP check with the mail service provider, which greatly increases the API speed. A syntax check and DNS check (MX records, A records) are still performed on the email address as well as our email risk scoring which detects disposable email addresses. This option is intended for services that require decision making in a time sensitive manner.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "abuse_strictness", + "type": "int", + "description": 'Set the strictness level for machine learning pattern recognition of abusive email addresses with the "recent_abuse" data point. Default level of 0 provides good coverage, however if you are filtering account applications and facing advanced fraudsters then we recommend increasing this value to level 1 or 2.', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "country", + "type": "str", + "description": "You can optionally provide us with the default country or countries this phone number is suspected to be associated with. Our system will prefer to use a country on this list for verification or will require a country to be specified in the event the phone number is less than 10 digits.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "ipqs_api_key", + "type": "str", + "description": "Your IPQS API key.", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_timeout", + "type": "int", + "description": "Maximum number of seconds to perform live page scanning and follow redirects. If your implementation requirements do not need an immediate response, we recommend bumping this value to 5. Default value is 2 seconds.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_strictness", + "type": "int", + "description": 'How strict should we scan this URL? Stricter checks may provide a higher false-positive rate. We recommend defaulting to level "0", the lowest strictness setting, and increasing to "1" or "2" depending on your levels of abuse.', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url_fast", + "type": "bool", + "description": "When enabled, the API will provide quicker response times using lighter checks and analysis. This setting defaults to false.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "phone_strictness", + "type": "int", + "description": 'How in depth (strict) do you want this reputation check to be? Stricter checks may provide a higher false-positive rate. We recommend starting at "0", the lowest strictness setting, and increasing to "1" or "2" depending on your levels of fraud.', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enhanced_name_check", + "type": "bool", + "description": "Please contact support to activate enhanced name appending for a phone number. Our standard phone validation service already includes extensive name appending data.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "ipqs.IPQualityScore", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "enhanced_line_check", + "type": "bool", + "description": 'Please contact support to activate this feature for more advanced active line checks through our HLR lookup service. This feature provides greater accuracy for identifying active or disconnected phone numbers including landline, mobile, and VOIP services. The "active_status" field is also populated when this feature is enabled.', + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + _create_object(Model, plugin) + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ("analyzers_manager", "0060_analyzer_config_ip2location"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0062_analyzer_config_mmdb_server.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0062_analyzer_config_mmdb_server.py new file mode 100644 index 0000000..42293c7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0062_analyzer_config_mmdb_server.py @@ -0,0 +1,137 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "module": "mmdb_server.MmdbServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Mmdb_server", + "description": "[mmdb-server](https://github.com/adulau/mmdb-server) is an open source fast API server to lookup IP addresses for their geographic location, AS number.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "mmdb_server.MmdbServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "base_url", + "type": "str", + "description": "base url for mmdb_server", + "is_secret": False, + "required": True, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "mmdb_server.MmdbServer", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "base_url", + "type": "str", + "description": "base url for mmdb_server", + "is_secret": False, + "required": True, + }, + "for_organization": False, + "value": "https://ip.circl.lu/geolookup/", + "updated_at": "2024-01-23T20:23:55.745858Z", + "owner": None, + "analyzer_config": "Mmdb_server", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + _create_object(Model, plugin) + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ("analyzers_manager", "0061_analyzer_config_ipqs_fraud_and_risk_scoring"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0063_alter_analyzerconfig_not_supported_filetypes_and_more.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0063_alter_analyzerconfig_not_supported_filetypes_and_more.py new file mode 100644 index 0000000..6df6062 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0063_alter_analyzerconfig_not_supported_filetypes_and_more.py @@ -0,0 +1,253 @@ +# Generated by Django 4.2.8 on 2024-01-22 10:10 + +from django.db import migrations, models +from django.db.models import Q + +import api_app.fields + + +def migrate(apps, schema_editor): + Job = apps.get_model("api_app", "Job") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + Job.objects.filter( + file_mimetype__in=["application/x-executable", "application/x-dosexec"] + ).update(file_mimetype="application/vnd.microsoft.portable-executable") + + for config in AnalyzerConfig.objects.filter( + Q( + not_supported_filetypes__contains=[ + "application/x-dosexec", + ] + ) + | Q( + not_supported_filetypes__contains=[ + "application/x-executable", + ] + ), + ): + try: + config.not_supported_filetypes.remove("application/x-executable") + except ValueError: + pass + try: + config.not_supported_filetypes.remove("application/x-dosexec") + except ValueError: + pass + config.not_supported_filetypes.append( + "application/vnd.microsoft.portable-executable" + ) + config.save() + + for config in AnalyzerConfig.objects.filter( + Q( + supported_filetypes__contains=[ + "application/x-dosexec", + ] + ) + | Q( + supported_filetypes__contains=[ + "application/x-executable", + ] + ), + ): + try: + config.supported_filetypes.remove("application/x-executable") + except ValueError: + pass + try: + config.supported_filetypes.remove("application/x-dosexec") + except ValueError: + pass + if config.name == "ELF_Info": + continue + config.supported_filetypes.append( + "application/vnd.microsoft.portable-executable" + ) + config.save() + + +def reverse_migrate(apps, schema_editor): + Job = apps.get_model("api_app", "Job") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + Job.objects.filter( + file_mimetype="application/vnd.microsoft.portable-executable" + ).update(file_mimetype="application/x-executable") + for config in AnalyzerConfig.objects.filter( + not_supported_filetypes__contains=[ + "application/vnd.microsoft.portable-executable" + ] + ): + config.not_supported_filetypes.remove( + "application/vnd.microsoft.portable-executable" + ) + config.not_supported_filetypes.append("application/x-executable") + config.save() + for config in AnalyzerConfig.objects.filter( + supported_filetypes__contains=["application/vnd.microsoft.portable-executable"] + ): + config.supported_filetypes.remove( + "application/vnd.microsoft.portable-executable" + ) + config.supported_filetypes.append("application/x-executable") + config.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0062_analyzer_config_mmdb_server"), + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ] + + operations = [ + migrations.AlterField( + model_name="analyzerconfig", + name="not_supported_filetypes", + field=api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("application/w-script-file", "Wscript"), + ("application/javascript", "Javascript1"), + ("application/x-javascript", "Javascript2"), + ("text/javascript", "Javascript3"), + ("application/x-vbscript", "Vb Script"), + ("text/x-ms-iqy", "Iqy"), + ("application/vnd.android.package-archive", "Apk"), + ("application/x-dex", "Dex"), + ("application/onenote", "One Note"), + ("application/zip", "Zip1"), + ("multipart/x-zip", "Zip2"), + ("application/java-archive", "Java"), + ("text/rtf", "Rtf1"), + ("application/rtf", "Rtf2"), + ("application/x-sharedlib", "Shared Lib"), + ("application/vnd.microsoft.portable-executable", "Exe"), + ("application/x-elf", "Elf"), + ("application/octet-stream", "Octet"), + ("application/vnd.tcpdump.pcap", "Pcap"), + ("application/pdf", "Pdf"), + ("text/html", "Html"), + ("application/x-mspublisher", "Pub"), + ("application/vnd.ms-excel.addin.macroEnabled", "Excel Macro1"), + ( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Excel Macro2", + ), + ("application/vnd.ms-excel", "Excel1"), + ("application/excel", "Excel2"), + ( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Doc", + ), + ("application/xml", "Xml1"), + ("text/xml", "Xml2"), + ("application/encrypted", "Encrypted"), + ("text/plain", "Plain"), + ("text/csv", "Csv"), + ( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Pptx", + ), + ("application/msword", "Word1"), + ( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Word2", + ), + ("application/vnd.ms-powerpoint", "Powerpoint"), + ("application/vnd.ms-office", "Office"), + ("application/x-binary", "Binary"), + ("application/x-macbinary", "Mac1"), + ("application/mac-binary", "Mac2"), + ("application/x-mach-binary", "Mac3"), + ("application/x-zip-compressed", "Compress1"), + ("application/x-compressed", "Compress2"), + ("application/vnd.ms-outlook", "Outlook"), + ("message/rfc822", "Eml"), + ("application/pkcs7-signature", "Pkcs7"), + ("application/x-pkcs7-signature", "Xpkcs7"), + ("multipart/mixed", "Mixed"), + ("text/x-shellscript", "X Shellscript"), + ], + max_length=90, + ), + blank=True, + default=list, + size=None, + ), + ), + migrations.AlterField( + model_name="analyzerconfig", + name="supported_filetypes", + field=api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("application/w-script-file", "Wscript"), + ("application/javascript", "Javascript1"), + ("application/x-javascript", "Javascript2"), + ("text/javascript", "Javascript3"), + ("application/x-vbscript", "Vb Script"), + ("text/x-ms-iqy", "Iqy"), + ("application/vnd.android.package-archive", "Apk"), + ("application/x-dex", "Dex"), + ("application/onenote", "One Note"), + ("application/zip", "Zip1"), + ("multipart/x-zip", "Zip2"), + ("application/java-archive", "Java"), + ("text/rtf", "Rtf1"), + ("application/rtf", "Rtf2"), + ("application/x-sharedlib", "Shared Lib"), + ("application/vnd.microsoft.portable-executable", "Exe"), + ("application/x-elf", "Elf"), + ("application/octet-stream", "Octet"), + ("application/vnd.tcpdump.pcap", "Pcap"), + ("application/pdf", "Pdf"), + ("text/html", "Html"), + ("application/x-mspublisher", "Pub"), + ("application/vnd.ms-excel.addin.macroEnabled", "Excel Macro1"), + ( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Excel Macro2", + ), + ("application/vnd.ms-excel", "Excel1"), + ("application/excel", "Excel2"), + ( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Doc", + ), + ("application/xml", "Xml1"), + ("text/xml", "Xml2"), + ("application/encrypted", "Encrypted"), + ("text/plain", "Plain"), + ("text/csv", "Csv"), + ( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Pptx", + ), + ("application/msword", "Word1"), + ( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Word2", + ), + ("application/vnd.ms-powerpoint", "Powerpoint"), + ("application/vnd.ms-office", "Office"), + ("application/x-binary", "Binary"), + ("application/x-macbinary", "Mac1"), + ("application/mac-binary", "Mac2"), + ("application/x-mach-binary", "Mac3"), + ("application/x-zip-compressed", "Compress1"), + ("application/x-compressed", "Compress2"), + ("application/vnd.ms-outlook", "Outlook"), + ("message/rfc822", "Eml"), + ("application/pkcs7-signature", "Pkcs7"), + ("application/x-pkcs7-signature", "Xpkcs7"), + ("multipart/mixed", "Mixed"), + ("text/x-shellscript", "X Shellscript"), + ], + max_length=90, + ), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0064_analyzer_config_zippy_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0064_analyzer_config_zippy_scan.py new file mode 100644 index 0000000..e2d28c6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0064_analyzer_config_zippy_scan.py @@ -0,0 +1,140 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "module": "zippy_scan.ZippyAnalyser", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "Zippy_scan", + "description": "[Zippy](https://github.com/thinkst/zippy): Fast method to classify text as AI or human-generated", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": [], + "supported_filetypes": ["text/plain"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "zippy_scan.ZippyAnalyser", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "engine", + "type": "str", + "description": "engine to be used for zippy", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "zippy_scan.ZippyAnalyser", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "engine", + "type": "str", + "description": "engine to be used for zippy", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "ensembled", + "updated_at": "2024-01-31T21:43:33.344103Z", + "owner": None, + "analyzer_config": "Zippy_scan", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + _create_object(Model, plugin) + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ( + "analyzers_manager", + "0063_alter_analyzerconfig_not_supported_filetypes_and_more", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0065_analyzer_config_validin.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0065_analyzer_config_validin.py new file mode 100644 index 0000000..59ec4f1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0065_analyzer_config_validin.py @@ -0,0 +1,147 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "module": "validin.Validin", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Validin", + "description": "(Validin's)[https://app.validin.com/docs] API for threat researchers, teams, and companies to investigate historic and current data describing the structure and composition of the internet.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": ["ip", "domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "validin.Validin", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "scan_choice", + "type": "str", + "description": "details at: https://app.validin.com/docs", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "validin.Validin", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "validin.Validin", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "scan_choice", + "type": "str", + "description": "details at: https://app.validin.com/docs", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Validin", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": "default", + "updated_at": "2024-02-05T23:09:25.617798Z", + "owner": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + _create_object(Model, plugin) + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ("analyzers_manager", "0064_analyzer_config_zippy_scan"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0066_analyzer_config_phoneinfoga.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0066_analyzer_config_phoneinfoga.py new file mode 100644 index 0000000..ba0fb6b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0066_analyzer_config_phoneinfoga.py @@ -0,0 +1,168 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "module": "phoneinfoga_scan.Phoneinfoga", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Phoneinfoga", + "description": "[Phoneinfoga](https://sundowndev.github.io/phoneinfoga/) is one of the most advanced tools to scan international phone numbers.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": True, + "maximum_tlp": "CLEAR", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "phoneinfoga_scan.Phoneinfoga", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "scanner_name", + "type": "str", + "description": "Scanner name for [Phoneinfoga](https://sundowndev.github.io/phoneinfoga/)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "phoneinfoga_scan.Phoneinfoga", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "NUMVERIFY_API_KEY", + "type": "str", + "description": "Numverify api key [Phoneinfoga](https://sundowndev.github.io/phoneinfoga/)", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "phoneinfoga_scan.Phoneinfoga", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "GOOGLECSE_CX", + "type": "str", + "description": "Googlecse_cx api key [Phoneinfoga](https://sundowndev.github.io/phoneinfoga/)", + "is_secret": True, + "required": False, + }, + { + "python_module": { + "module": "phoneinfoga_scan.Phoneinfoga", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "GOOGLE_API_KEY", + "type": "str", + "description": "Google api key [Phoneinfoga](https://sundowndev.github.io/phoneinfoga/)", + "is_secret": True, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "phoneinfoga_scan.Phoneinfoga", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "scanner_name", + }, + "for_organization": False, + "value": "local", + "updated_at": "2024-02-04T09:40:51.675021Z", + "owner": None, + "analyzer_config": "Phoneinfoga", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + _create_object(Model, plugin) + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ( + "analyzers_manager", + "0065_analyzer_config_validin", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0067_update_misp.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0067_update_misp.py new file mode 100644 index 0000000..d79bc40 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0067_update_misp.py @@ -0,0 +1,104 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + + pm = PythonModule.objects.get( + module="misp.MISP", base_path="api_app.analyzers_manager.observable_analyzers" + ) + + p1 = Parameter( + name="timeout", + type="int", + description="set timeout for misp instance", + is_secret=False, + required=True, + python_module=pm, + ) + p1.full_clean() + p1.save() + + p2 = Parameter( + name="published", + type="bool", + description="get only published events", + is_secret=False, + required=True, + python_module=pm, + ) + p2.full_clean() + p2.save() + + p3 = Parameter( + name="metadata", + type="bool", + description="have lighter queries but less data", + is_secret=False, + required=True, + python_module=pm, + ) + p3.full_clean() + p3.save() + + for analyzer in pm.analyzerconfigs.all(): + pc1 = PluginConfig( + value=5, + analyzer_config=analyzer, + for_organization=False, + owner=None, + parameter=p1, + ) + pc1.full_clean() + pc1.save() + + pc2 = PluginConfig( + value=False, + analyzer_config=analyzer, + for_organization=False, + owner=None, + parameter=p2, + ) + pc2.full_clean() + pc2.save() + + pc3 = PluginConfig( + value=False, + analyzer_config=analyzer, + for_organization=False, + owner=None, + parameter=p3, + ) + pc3.full_clean() + pc3.save() + + +def reverse_migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + + pm = PythonModule.objects.get( + module="misp.MISP", base_path="api_app.analyzers_manager.observable_analyzers" + ) + parameters_to_remove = Parameter.objects.filter( + python_module=pm, name__in=["timeout", "published", "metadata"] + ) + + PluginConfig.objects.filter( + parameter__python_module=pm, parameter__in=parameters_to_remove + ).delete() + + parameters_to_remove.delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ("analyzers_manager", "0066_analyzer_config_phoneinfoga"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0068_analyzer_config_feodo_tracker.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0068_analyzer_config_feodo_tracker.py new file mode 100644 index 0000000..d05720a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0068_analyzer_config_feodo_tracker.py @@ -0,0 +1,181 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "module": "feodo_tracker.Feodo_Tracker", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Feodo_Tracker", + "description": "[Feodo Tracker](https://feodotracker.abuse.ch/) offers various blocklists,\r\n helping network owners to protect their\r\n users from Dridex and Emotet/Heodo.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "health_check_task": None, + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "feodo_tracker.Feodo_Tracker", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "use_recommended_url", + "type": "bool", + "description": "use recommended [db](https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.json)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "feodo_tracker.Feodo_Tracker", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "update_on_run", + "type": "bool", + "description": "update analyzer db on every run (Analyzer db\r\n is updated once in every 24 hours by default).", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "feodo_tracker.Feodo_Tracker", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "use_recommended_url", + "type": "bool", + "description": "use recommended [db](https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.json)", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Feodo_Tracker", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": False, + "updated_at": "2024-02-15T03:48:59.096424Z", + "owner": None, + }, + { + "parameter": { + "python_module": { + "module": "feodo_tracker.Feodo_Tracker", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "update_on_run", + "type": "bool", + "description": "update analyzer db on every run (Analyzer db\r\n is updated once in every 24 hours by default).", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Feodo_Tracker", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": False, + "updated_at": "2024-02-15T03:48:59.113563Z", + "owner": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ("analyzers_manager", "0067_update_misp"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0069_analyzer_config_bgp_ranking.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0069_analyzer_config_bgp_ranking.py new file mode 100644 index 0000000..f140a6b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0069_analyzer_config_bgp_ranking.py @@ -0,0 +1,224 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": { + "minute": "0", + "hour": "*", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_schedule": None, + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "BGP_Ranking", + "description": "[BGP Ranking](https://github.com/D4-project/BGP-Ranking) provides a way to collect such malicious activities, aggregate the information per ASN and provide a ranking model to rank the ASN from the most malicious to the less malicious ASN.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "period", + "type": "int", + "description": "period for bgp-ranking in days", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "timeout for bgp-ranking in seconds", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url", + "type": "str", + "description": "url for bgp-ranking default is https://bgpranking-ng.circl.lu", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "period", + "type": "int", + "description": "period for bgp-ranking in days", + "is_secret": False, + "required": False, + }, + "analyzer_config": "BGP_Ranking", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": 0, + "updated_at": "2024-03-13T16:00:13.078391Z", + "owner": None, + }, + { + "parameter": { + "python_module": { + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "timeout", + "type": "int", + "description": "timeout for bgp-ranking in seconds", + "is_secret": False, + "required": False, + }, + "analyzer_config": "BGP_Ranking", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": 5, + "updated_at": "2024-03-13T16:00:35.931795Z", + "owner": None, + }, + { + "parameter": { + "python_module": { + "module": "bgp_ranking.BGPRanking", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "url", + "type": "str", + "description": "url for bgp-ranking default is https://bgpranking-ng.circl.lu", + "is_secret": False, + "required": False, + }, + "analyzer_config": "BGP_Ranking", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": "https://bgpranking-ng.circl.lu", + "updated_at": "2024-03-13T16:35:34.870882Z", + "owner": None, + }, +] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ("analyzers_manager", "0068_analyzer_config_feodo_tracker"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0070_urlhaus_threatfox_disable_param.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0070_urlhaus_threatfox_disable_param.py new file mode 100644 index 0000000..60a7526 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0070_urlhaus_threatfox_disable_param.py @@ -0,0 +1,85 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + pm = PythonModule.objects.get( + module="urlhaus.URLHaus", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + p = Parameter( + name="disable", + type="bool", + description="Disable the analyzer at runtime", + is_secret=False, + required=False, + python_module=pm, + ) + p.full_clean() + p.save() + + for analyzer in pm.analyzerconfigs.all(): + pc1 = PluginConfig( + value=False, + analyzer_config=analyzer, + for_organization=False, + owner=None, + parameter=p, + ) + pc1.full_clean() + pc1.save() + + pm = PythonModule.objects.get( + module="threatfox.ThreatFox", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + p = Parameter( + name="disable", + type="bool", + description="Disable the analyzer at runtime", + is_secret=False, + required=False, + python_module=pm, + ) + p.full_clean() + p.save() + + for analyzer in pm.analyzerconfigs.all(): + pc1 = PluginConfig( + value=False, + analyzer_config=analyzer, + for_organization=False, + owner=None, + parameter=p, + ) + pc1.full_clean() + pc1.save() + + +def reverse_migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + Parameter = apps.get_model("api_app", "Parameter") + + pm = PythonModule.objects.get( + module="urlhaus.URLHaus", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + Parameter.objects.get(name="disable", python_module=pm).delete() + + pm = PythonModule.objects.get( + module="threatfox.ThreatFox", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + Parameter.objects.get(name="disable", python_module=pm).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ("analyzers_manager", "0069_analyzer_config_bgp_ranking"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0071_analyzer_config_tor_nodes_danmeuk.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0071_analyzer_config_tor_nodes_danmeuk.py new file mode 100644 index 0000000..69353aa --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0071_analyzer_config_tor_nodes_danmeuk.py @@ -0,0 +1,123 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "0", + "hour": "*/6", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "module": "tor_nodes_danmeuk.TorNodesDanMeUK", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Tor_Nodes_DanMeUk", + "description": "check if an IP is a Tor Node", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ("analyzers_manager", "0070_urlhaus_threatfox_disable_param"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0072_analyzer_config_tweetfeed.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0072_analyzer_config_tweetfeed.py new file mode 100644 index 0000000..0f268c1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0072_analyzer_config_tweetfeed.py @@ -0,0 +1,152 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "module": "tweetfeeds.TweetFeeds", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "TweetFeed", + "description": "[TweetFeed](https://tweetfeed.live/) collects Indicators of Compromise (IOCs) shared by the infosec community at Twitter.\r\nHere you will find malicious URLs, domains, IPs, and SHA256/MD5 hashes.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "CLEAR", + "observable_supported": ["ip", "url", "domain", "hash"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "tweetfeeds.TweetFeeds", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "time", + "type": "str", + "description": "time for TweetFeed.\r\n[today (Today starting 00:00 UTC),week (Last 7 days),month (Last 30 days),year (Last 365 days)]", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "tweetfeeds.TweetFeeds", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "filter1", + "type": "str", + "description": "filter1 for TweetFeed\r\n[Can be an specific user, type or tag.\r\nTag (phishing / ransomware / CobaltStrike ...),\r\nUser (@malwrhunterteam / @1ZRR4H / @MBThreatIntel ...)]", + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ("analyzers_manager", "0071_analyzer_config_tor_nodes_danmeuk"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0073_remove_dragonfly_analyzer.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0073_remove_dragonfly_analyzer.py new file mode 100644 index 0000000..cf3ac0b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0073_remove_dragonfly_analyzer.py @@ -0,0 +1,25 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + pm = PythonModule.objects.get( + module="dragonfly.DragonflyEmulation", + base_path="api_app.analyzers_manager.file_analyzers", + ) + pm.analyzerconfigs.all().delete() + pm.delete() + + +def reverse_migrate(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0072_analyzer_config_tweetfeed"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0074_adjust_maximum_tlp.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0074_adjust_maximum_tlp.py new file mode 100644 index 0000000..0c59cc0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0074_adjust_maximum_tlp.py @@ -0,0 +1,133 @@ +from django.db import migrations + +from api_app.choices import TLP + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + + pm = PythonModule.objects.get( + module="bgp_ranking.BGPRanking", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.AMBER + analyzer.save() + + pm = PythonModule.objects.get( + module="feodo_tracker.Feodo_Tracker", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.RED + analyzer.save() + + pm = PythonModule.objects.get( + module="mmdb_server.MmdbServer", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.AMBER + analyzer.save() + + pm = PythonModule.objects.get( + module="phoneinfoga_scan.Phoneinfoga", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.AMBER + analyzer.save() + + pm = PythonModule.objects.get( + module="tweetfeeds.TweetFeeds", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.RED + analyzer.save() + + pm = PythonModule.objects.get( + module="validin.Validin", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.AMBER + analyzer.save() + + pm = PythonModule.objects.get( + module="zippy_scan.ZippyAnalyser", + base_path="api_app.analyzers_manager.file_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.RED + analyzer.save() + + +def reverse_migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + + pm = PythonModule.objects.get( + module="bgp_ranking.BGPRanking", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + pm = PythonModule.objects.get( + module="feodo_tracker.Feodo_Tracker", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + pm = PythonModule.objects.get( + module="mmdb_server.MmdbServer", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + pm = PythonModule.objects.get( + module="phoneinfoga_scan.Phoneinfoga", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + pm = PythonModule.objects.get( + module="tweetfeeds.TweetFeeds", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + pm = PythonModule.objects.get( + module="validin.Validin", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + pm = PythonModule.objects.get( + module="zippy_scan.ZippyAnalyser", + base_path="api_app.analyzers_manager.file_analyzers", + ) + for analyzer in pm.analyzerconfigs.all(): + analyzer.maximum_tlp = TLP.CLEAR + analyzer.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0073_remove_dragonfly_analyzer"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0075_adjust_greynoise.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0075_adjust_greynoise.py new file mode 100644 index 0000000..e917ec7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0075_adjust_greynoise.py @@ -0,0 +1,28 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + + pm = PythonModule.objects.get( + module="greynoiseintel.GreyNoiseAnalyzer", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + param = pm.parameters.get(name="api_key_name") + param.required = False + param.values.filter(owner=None, for_organization=False).delete() + param.save() + + +def reverse_migrate(apps, schema_editor): + ... + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0074_adjust_maximum_tlp"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0076_analyzer_config_greynoise_labs.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0076_analyzer_config_greynoise_labs.py new file mode 100644 index 0000000..65bde2f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0076_analyzer_config_greynoise_labs.py @@ -0,0 +1,135 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "0", + "hour": "*/6", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "module": "greynoise_labs.GreynoiseLabs", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Greynoise_Labs", + "description": "scan an IP against the [Greynoise Labs API](https://www.greynoise.io/) (requires authentication token obtained from cookies on greynoise website)", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "greynoise_labs.GreynoiseLabs", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "auth_token", + "type": "str", + "description": "Authentication token obtained from cookies on greynoise website.", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0075_adjust_greynoise"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0077_analyzer_config_abusix.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0077_analyzer_config_abusix.py new file mode 100644 index 0000000..9e211ae --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0077_analyzer_config_abusix.py @@ -0,0 +1,117 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "abusix.Abusix", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Abusix", + "description": "get abuse contacts of an IP from [Abusix](https://abusix.com/contact-db/)", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": False, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0076_analyzer_config_greynoise_labs"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0078_analyzer_config_hfinger.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0078_analyzer_config_hfinger.py new file mode 100644 index 0000000..719a890 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0078_analyzer_config_hfinger.py @@ -0,0 +1,152 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "hfinger.Hfinger", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "Hfinger", + "description": "create fingerprints of malware HTTPs requests using [Hfinger](https://github.com/CERT-Polska/hfinger)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": ["application/vnd.tcpdump.pcap"], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "hfinger.Hfinger", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "fingerprint_report_mode", + "type": "int", + "description": "Fingerprint report mode. \r\n0 - similar number of collisions and fingerprints as mode 2, but using fewer features, \r\n1 - representation of all designed features, but a little more collisions than modes 0, 2, and 4, \r\n2 - optimal (the default mode), \r\n3 - the lowest number of generated fingerprints, but the highest number of collisions, \r\n4 - the highest fingerprint entropy, but slightly more fingerprints than modes 0-2", + "is_secret": False, + "required": False, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "hfinger.Hfinger", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "fingerprint_report_mode", + "type": "int", + "description": "Fingerprint report mode. \r\n0 - similar number of collisions and fingerprints as mode 2, but using fewer features, \r\n1 - representation of all designed features, but a little more collisions than modes 0, 2, and 4, \r\n2 - optimal (the default mode), \r\n3 - the lowest number of generated fingerprints, but the highest number of collisions, \r\n4 - the highest fingerprint entropy, but slightly more fingerprints than modes 0-2", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Hfinger", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": 2, + "updated_at": "2024-04-03T19:33:51.679066Z", + "owner": None, + } +] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0077_analyzer_config_abusix"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0079_remove_dns0_rrsets_analyzer.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0079_remove_dns0_rrsets_analyzer.py new file mode 100644 index 0000000..25df9bb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0079_remove_dns0_rrsets_analyzer.py @@ -0,0 +1,27 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + pm = PythonModule.objects.filter( + module="dns0.dns0_rrsets.DNS0Rrsets", + base_path="api_app.analyzers_manager.observable_analyzers", + ).first() + if pm: + pm.analyzerconfigs.all().delete() + pm.delete() + + +def reverse_migrate(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("playbooks_manager", "0032_delete_dns0_playbook_free_to_use_analyzers"), + ("analyzers_manager", "0078_analyzer_config_hfinger"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0080_remove_dns0_names_analyzer.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0080_remove_dns0_names_analyzer.py new file mode 100644 index 0000000..8868a52 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0080_remove_dns0_names_analyzer.py @@ -0,0 +1,27 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + pm = PythonModule.objects.filter( + module="dns0.dns0_names.DNS0Names", + base_path="api_app.analyzers_manager.observable_analyzers", + ).first() + if pm: + pm.analyzerconfigs.all().delete() + pm.delete() + + +def reverse_migrate(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("playbooks_manager", "0032_delete_dns0_playbook_free_to_use_analyzers"), + ("analyzers_manager", "0079_remove_dns0_rrsets_analyzer"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0081_adjust_abusix.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0081_adjust_abusix.py new file mode 100644 index 0000000..c32e125 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0081_adjust_abusix.py @@ -0,0 +1,23 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + + AnalyzerConfig.objects.filter( + name="Abusix", + ).update(health_check_status=True) + + +def reverse_migrate(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0080_remove_dns0_names_analyzer"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0082_analyzer_config_ip2whois.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0082_analyzer_config_ip2whois.py new file mode 100644 index 0000000..dba0ab1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0082_analyzer_config_ip2whois.py @@ -0,0 +1,131 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "ip2whois.Ip2whois", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "IP2WHOIS", + "description": "[API Docs](https://www.ip2location.io/ip2whois-documentation)" + " IP2Location.io IP2WHOIS Domain WHOIS API helps users to obtain" + " domain information and WHOIS record by using a domain name.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ip2whois.Ip2whois", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "IP2WHOIS API key name.", + "is_secret": True, + "required": True, + } +] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0081_adjust_abusix"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0083_adjust_docinfo.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0083_adjust_docinfo.py new file mode 100644 index 0000000..d26b6e1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0083_adjust_docinfo.py @@ -0,0 +1,25 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + + ac = AnalyzerConfig.objects.get( + name="Doc_Info", + ) + ac.supported_filetypes.remove("application/onenote") + ac.save() + + +def reverse_migrate(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0082_analyzer_config_ip2whois"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0084_alter_analyzerconfig_not_supported_filetypes_and_more.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0084_alter_analyzerconfig_not_supported_filetypes_and_more.py new file mode 100644 index 0000000..5385270 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0084_alter_analyzerconfig_not_supported_filetypes_and_more.py @@ -0,0 +1,169 @@ +# Generated by Django 4.2.11 on 2024-04-11 14:07 + +from django.db import migrations, models + +import api_app.fields + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0083_adjust_docinfo"), + ] + + operations = [ + migrations.AlterField( + model_name="analyzerconfig", + name="not_supported_filetypes", + field=api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("application/w-script-file", "Wscript"), + ("application/javascript", "Javascript1"), + ("application/x-javascript", "Javascript2"), + ("text/javascript", "Javascript3"), + ("application/x-vbscript", "Vb Script"), + ("text/x-ms-iqy", "Iqy"), + ("application/vnd.android.package-archive", "Apk"), + ("application/x-dex", "Dex"), + ("application/onenote", "One Note"), + ("application/zip", "Zip1"), + ("multipart/x-zip", "Zip2"), + ("application/java-archive", "Java"), + ("text/rtf", "Rtf1"), + ("application/rtf", "Rtf2"), + ("application/x-sharedlib", "Shared Lib"), + ("application/vnd.microsoft.portable-executable", "Exe"), + ("application/x-elf", "Elf"), + ("application/octet-stream", "Octet"), + ("application/vnd.tcpdump.pcap", "Pcap"), + ("application/pdf", "Pdf"), + ("text/html", "Html"), + ("application/x-mspublisher", "Pub"), + ("application/vnd.ms-excel.addin.macroEnabled", "Excel Macro1"), + ( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Excel Macro2", + ), + ("application/vnd.ms-excel", "Excel1"), + ("application/excel", "Excel2"), + ( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Doc", + ), + ("application/xml", "Xml1"), + ("text/xml", "Xml2"), + ("application/encrypted", "Encrypted"), + ("text/plain", "Plain"), + ("text/csv", "Csv"), + ( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Pptx", + ), + ("application/msword", "Word1"), + ( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Word2", + ), + ("application/vnd.ms-powerpoint", "Powerpoint"), + ("application/vnd.ms-office", "Office"), + ("application/x-binary", "Binary"), + ("application/x-macbinary", "Mac1"), + ("application/mac-binary", "Mac2"), + ("application/x-mach-binary", "Mac3"), + ("application/x-zip-compressed", "Compress1"), + ("application/x-compressed", "Compress2"), + ("application/vnd.ms-outlook", "Outlook"), + ("message/rfc822", "Eml"), + ("application/pkcs7-signature", "Pkcs7"), + ("application/x-pkcs7-signature", "Xpkcs7"), + ("multipart/mixed", "Mixed"), + ("text/x-shellscript", "X Shellscript"), + ("application/x-chrome-extension", "Crx"), + ("application/json", "Json"), + ], + max_length=90, + ), + blank=True, + default=list, + size=None, + ), + ), + migrations.AlterField( + model_name="analyzerconfig", + name="supported_filetypes", + field=api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("application/w-script-file", "Wscript"), + ("application/javascript", "Javascript1"), + ("application/x-javascript", "Javascript2"), + ("text/javascript", "Javascript3"), + ("application/x-vbscript", "Vb Script"), + ("text/x-ms-iqy", "Iqy"), + ("application/vnd.android.package-archive", "Apk"), + ("application/x-dex", "Dex"), + ("application/onenote", "One Note"), + ("application/zip", "Zip1"), + ("multipart/x-zip", "Zip2"), + ("application/java-archive", "Java"), + ("text/rtf", "Rtf1"), + ("application/rtf", "Rtf2"), + ("application/x-sharedlib", "Shared Lib"), + ("application/vnd.microsoft.portable-executable", "Exe"), + ("application/x-elf", "Elf"), + ("application/octet-stream", "Octet"), + ("application/vnd.tcpdump.pcap", "Pcap"), + ("application/pdf", "Pdf"), + ("text/html", "Html"), + ("application/x-mspublisher", "Pub"), + ("application/vnd.ms-excel.addin.macroEnabled", "Excel Macro1"), + ( + "application/vnd.ms-excel.sheet.macroEnabled.12", + "Excel Macro2", + ), + ("application/vnd.ms-excel", "Excel1"), + ("application/excel", "Excel2"), + ( + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "Doc", + ), + ("application/xml", "Xml1"), + ("text/xml", "Xml2"), + ("application/encrypted", "Encrypted"), + ("text/plain", "Plain"), + ("text/csv", "Csv"), + ( + "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "Pptx", + ), + ("application/msword", "Word1"), + ( + "application/vnd.openxmlformats-officedocument.wordprocessingml.document", + "Word2", + ), + ("application/vnd.ms-powerpoint", "Powerpoint"), + ("application/vnd.ms-office", "Office"), + ("application/x-binary", "Binary"), + ("application/x-macbinary", "Mac1"), + ("application/mac-binary", "Mac2"), + ("application/x-mach-binary", "Mac3"), + ("application/x-zip-compressed", "Compress1"), + ("application/x-compressed", "Compress2"), + ("application/vnd.ms-outlook", "Outlook"), + ("message/rfc822", "Eml"), + ("application/pkcs7-signature", "Pkcs7"), + ("application/x-pkcs7-signature", "Xpkcs7"), + ("multipart/mixed", "Mixed"), + ("text/x-shellscript", "X Shellscript"), + ("application/x-chrome-extension", "Crx"), + ("application/json", "Json"), + ], + max_length=90, + ), + blank=True, + default=list, + size=None, + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0085_analyzer_config_permhash.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0085_analyzer_config_permhash.py new file mode 100644 index 0000000..be8d239 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0085_analyzer_config_permhash.py @@ -0,0 +1,127 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "perm_hash.Permhash", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "Permhash", + "description": "create a hash of permissions in APK, Android manifest, chrome extension, chrome extension manifest files using [permhash](https://github.com/google/permhash).", + "disabled": False, + "soft_time_limit": 20, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/vnd.android.package-archive", + "application/zip", + "application/java-archive", + "application/octet-stream", + "text/plain", + "application/x-chrome-extension", + "application/json", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ( + "analyzers_manager", + "0084_alter_analyzerconfig_not_supported_filetypes_and_more", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0086_analyzer_config_blint.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0086_analyzer_config_blint.py new file mode 100644 index 0000000..d9192ab --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0086_analyzer_config_blint.py @@ -0,0 +1,128 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "blint_scan.BlintAnalyzer", + "base_path": "api_app.analyzers_manager.file_analyzers", + }, + "name": "Blint", + "description": "[Blint](https://github.com/owasp-dep-scan/blint) is a Binary Linter that checks the security properties and capabilities of your executables.\r\nSupported binary formats:\r\n- Android (apk, aab)\r\n- ELF (GNU, musl)\r\n- PE (exe, dll)\r\n- Mach-O (x64, arm64)", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "file", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": [], + "supported_filetypes": [ + "application/vnd.android.package-archive", + "application/vnd.microsoft.portable-executable", + "application/x-binary", + "application/x-macbinary", + "application/mac-binary", + "application/x-mach-binary", + "application/x-elf", + "application/x-sharedlib", + "application/java-archive", + "application/x-dex", + "application/zip", + ], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("analyzers_manager", "0085_analyzer_config_permhash"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0087_alter_mmdbserver_param.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0087_alter_mmdbserver_param.py new file mode 100644 index 0000000..d0b8a4b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0087_alter_mmdbserver_param.py @@ -0,0 +1,26 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + + pm = PythonModule.objects.get( + module="mmdb_server.MmdbServer", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + param = pm.parameters.get(name="base_url") + param.name = "url" + param.save() + + +def reverse_migrate(apps, schema_editor): + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0086_analyzer_config_blint"), + ] + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0088_phoneinfoga_parameters.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0088_phoneinfoga_parameters.py new file mode 100644 index 0000000..e4ceead --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0088_phoneinfoga_parameters.py @@ -0,0 +1,53 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + PythonModule = apps.get_model("api_app", "PythonModule") + pm = PythonModule.objects.get( + module="phoneinfoga_scan.Phoneinfoga", + base_path="api_app.analyzers_manager.observable_analyzers", + ) + Parameter.objects.create( + name="googlecse_max_results", + type="int", + description="Number of Google results for [Phoneinfoga](https://sundowndev.github.io/phoneinfoga/)", + is_secret=False, + required=False, + python_module=pm, + ) + p2 = Parameter.objects.create( + name="scanners", + type="list", + description="List of scanner names for [Phoneinfoga](https://sundowndev.github.io/phoneinfoga/). Available options are: `local,numverify,googlecse,ovh`", + is_secret=False, + required=False, + python_module=pm, + ) + p3 = Parameter.objects.get(name="scanner_name", python_module=pm) + for config in pm.analyzerconfigs.all(): + pcs = PluginConfig.objects.filter(analyzer_config=config, parameter=p3) + for pc in pcs: + pc.value = [pc.value] + pc.parameter = p2 + pc.save() + p3.delete() + Parameter.objects.create( + name="all_scanners", + type="bool", + description="Set this to True to enable all available scanners. " + "If enabled, this overwrite the scanner param", + is_secret=False, + required=False, + python_module=pm, + ) + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("analyzers_manager", "0087_alter_mmdbserver_param"), + ] + + operations = [migrations.RunPython(migrate, migrations.RunPython.noop)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0089_analyzer_config_hudsonrock.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0089_analyzer_config_hudsonrock.py new file mode 100644 index 0000000..51f2ebe --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0089_analyzer_config_hudsonrock.py @@ -0,0 +1,245 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_schedule": None, + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "HudsonRock", + "description": "[Hudson Rock](https://cavalier.hudsonrock.com/docs) provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors. The database is updated with new compromised computers every day, offering cybersecurity providers the ability to alert security teams ahead of imminent attacks, when users get compromised and have their credentials stolen.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["ip", "domain", "generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "compromised_since", + "type": "str", + "description": "ISO Date: YYYY-MM-DDThh:mm:ss.sssZ\r\ne.g: 2024-05-17T11:22:59.180Z\r\ndefault: All Time", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "compromised_until", + "type": "str", + "description": "ISO Date: YYYY-MM-DDThh:mm:ss.sssZ\r\ne.g: 2024-05-17T11:22:59.180Z\r\ndefault: All Time", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "page", + "type": "int", + "description": "The API utilises data pagination, where a maximum of 50 documents (stealers) per request are returned. When querying for a specific page, such as page 2, the API will skip the first 50 documents and return the next 50.\r\ndefault : 1", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "added_since", + "type": "str", + "description": "ISO Date: YYYY-MM-DDThh:mm:ss.sssZ\r\ne.g: 2024-05-17T11:22:59.180Z\r\ndefault: All Time", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "added_until", + "type": "str", + "description": "ISO Date: YYYY-MM-DDThh:mm:ss.sssZ\r\ne.g: 2024-05-17T11:22:59.180Z\r\ndefault: All Time", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "installed_software", + "type": "bool", + "description": "When set to true, installed software from the compromised computer will be shown.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "sort_by", + "type": "str", + "description": "Options/Data Type: \r\n1.date_compromised\r\n2.date_uploaded\r\nDefault: date_compromised\r\nThe API allows for sorting of the machine records by date of compromise or date added to Hudson Rock's system, with the results being returned in descending order.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "domain_cred_type", + "type": "str", + "description": "Options/Data Type: \r\n1.employees\r\n2.users\r\n3. all(default)\r\nCavalier supports two type of credentials: Employees and users (APKs are considered as ‘user’ type). Filtering displays only one type for the desired domain", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "domain_filtered", + "type": "bool", + "description": 'Filter results to show only credentials which are related to the specified domain/s.\r\n*This is only applicable for when "type" parameter is set to "employees".', + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "hudsonrock.HudsonRock", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "third_party_domains", + "type": "bool", + "description": 'When set to true, corporate credentials of compromised employees of the searched domain found in external domains will be shown, i.e - in a search for company.com john@company.com logging into zoom.us will be shown.\r\n*This is only applicable for when "type" parameter is set to "employees".', + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0088_phoneinfoga_parameters"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0090_analyzer_config_cycat.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0090_analyzer_config_cycat.py new file mode 100644 index 0000000..7be5a34 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0090_analyzer_config_cycat.py @@ -0,0 +1,117 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "cycat.CyCat", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "CyCat", + "description": "[CyCat](https://cycat.org/) or the CYbersecurity Resource CATalogue aims at mapping and documenting, in a single formalism and catalogue available cybersecurity tools, rules, playbooks, processes and controls.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0089_analyzer_config_hudsonrock"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0091_analyzer_config_vulners.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0091_analyzer_config_vulners.py new file mode 100644 index 0000000..98efca1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0091_analyzer_config_vulners.py @@ -0,0 +1,235 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": { + "minute": "0", + "hour": "0", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_schedule": None, + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "Vulners", + "description": "[Vulners](vulners.com) is the most complete and the only fully correlated security intelligence database, which goes through constant updates and links 200+ data sources in a unified machine-readable format. It contains 8 mln+ entries, including CVEs, advisories, exploits, and IoCs — everything you need to stay abreast on the latest security threats.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "AMBER", + "observable_supported": ["generic"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "score_AI", + "type": "bool", + "description": "Score any vulnerability with Vulners AI.\r\nDefault: False", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "api_key_name", + "type": "str", + "description": "api key for vulners", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "skip", + "type": "int", + "description": "skip parameter for vulners analyzer", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "size", + "type": "int", + "description": "size parameter for vulners analyzer", + "is_secret": False, + "required": False, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "score_AI", + "type": "bool", + "description": "Score any vulnerability with Vulners AI.\r\nDefault: False", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Vulners", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": False, + "updated_at": "2024-05-22T18:49:52.056060Z", + "owner": None, + }, + { + "parameter": { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "skip", + "type": "int", + "description": "skip parameter for vulners analyzer", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Vulners", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": 0, + "updated_at": "2024-05-23T06:45:24.105426Z", + "owner": None, + }, + { + "parameter": { + "python_module": { + "module": "vulners.Vulners", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "size", + "type": "int", + "description": "size parameter for vulners analyzer", + "is_secret": False, + "required": False, + }, + "analyzer_config": "Vulners", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": 5, + "updated_at": "2024-05-23T06:45:24.109831Z", + "owner": None, + }, +] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0090_analyzer_config_cycat"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0092_alter_validin_desc.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0092_alter_validin_desc.py new file mode 100644 index 0000000..a7476a1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0092_alter_validin_desc.py @@ -0,0 +1,36 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + plugin_name = "Validin" + correct_description = "[Validin's](https://app.validin.com) API for threat researchers, teams, and companies to investigate historic and current data describing the structure and composition of the internet." + + try: + plugin = AnalyzerConfig.objects.get(name=plugin_name) + plugin.description = correct_description + plugin.save() + except AnalyzerConfig.DoesNotExist: + pass + + +def reverse_migrate(apps, schema_editor): + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + plugin_name = "Validin" + original_description = "(Validin's)[https://app.validin.com/docs] API for threat researchers, teams, and companies to investigate historic and current data describing the structure and composition of the internet." + + try: + plugin = AnalyzerConfig.objects.get(name=plugin_name) + plugin.description = original_description + plugin.save() + except AnalyzerConfig.DoesNotExist: + pass + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ("analyzers_manager", "0091_analyzer_config_vulners"), + ] + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0093_analyzer_config_ailtyposquatting.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0093_analyzer_config_ailtyposquatting.py new file mode 100644 index 0000000..fc7ad9d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/0093_analyzer_config_ailtyposquatting.py @@ -0,0 +1,151 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "ailtyposquatting.AilTypoSquatting", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "AILTypoSquatting", + "description": "[AILTypoSquatting](https://github.com/typosquatter/ail-typo-squatting) is a Python library to generate list of potential typo squatting domains with domain name permutation engine to feed AIL and other systems.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "type": "observable", + "docker_based": False, + "maximum_tlp": "RED", + "observable_supported": ["domain"], + "supported_filetypes": [], + "run_hash": False, + "run_hash_type": "", + "not_supported_filetypes": [], + "model": "analyzers_manager.AnalyzerConfig", +} + +params = [ + { + "python_module": { + "module": "ailtyposquatting.AilTypoSquatting", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dns_resolving", + "type": "bool", + "description": "dns_resolving for AilTypoSquatting; only works for TLP CLEAR", + "is_secret": False, + "required": False, + }, +] +values = [ + { + "parameter": { + "python_module": { + "module": "ailtyposquatting.AilTypoSquatting", + "base_path": "api_app.analyzers_manager.observable_analyzers", + }, + "name": "dns_resolving", + "type": "bool", + "description": "dns_resolving for AilTypoSquatting; only works for TLP CLEAR", + "is_secret": False, + "required": False, + }, + "analyzer_config": "AILTypoSquatting", + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + "for_organization": False, + "value": False, + "updated_at": "2024-05-26T00:10:15.236358Z", + "owner": None, + }, +] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("analyzers_manager", "0092_alter_validin_desc"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/models.py b/Submodules/IntelOwl/api_app/analyzers_manager/models.py new file mode 100644 index 0000000..eb1d762 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/models.py @@ -0,0 +1,226 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from logging import getLogger +from typing import Optional + +from django.contrib.contenttypes.fields import GenericRelation +from django.core.exceptions import ValidationError +from django.db import models + +from api_app.analyzers_manager.constants import ( + HashChoices, + ObservableTypes, + TypeChoices, +) +from api_app.analyzers_manager.exceptions import AnalyzerConfigurationException +from api_app.analyzers_manager.queryset import AnalyzerReportQuerySet +from api_app.choices import TLP, PythonModuleBasePaths +from api_app.fields import ChoiceArrayField +from api_app.models import AbstractReport, PythonConfig, PythonModule + +logger = getLogger(__name__) + + +class AnalyzerReport(AbstractReport): + objects = AnalyzerReportQuerySet.as_manager() + config = models.ForeignKey( + "AnalyzerConfig", related_name="reports", null=False, on_delete=models.CASCADE + ) + + class Meta: + unique_together = [("config", "job")] + indexes = AbstractReport.Meta.indexes + + +class MimeTypes(models.TextChoices): + # IMPORTANT! in case you update this Enum remember to update also the frontend + WSCRIPT = "application/w-script-file" + JAVASCRIPT1 = "application/javascript" + JAVASCRIPT2 = "application/x-javascript" + JAVASCRIPT3 = "text/javascript" + VB_SCRIPT = "application/x-vbscript" + IQY = "text/x-ms-iqy" + APK = "application/vnd.android.package-archive" + DEX = "application/x-dex" + ONE_NOTE = "application/onenote" + ZIP1 = "application/zip" + ZIP2 = "multipart/x-zip" + JAVA = "application/java-archive" + RTF1 = "text/rtf" + RTF2 = "application/rtf" + SHARED_LIB = "application/x-sharedlib" + EXE = "application/vnd.microsoft.portable-executable" + ELF = "application/x-elf" + OCTET = "application/octet-stream" + PCAP = "application/vnd.tcpdump.pcap" + PDF = "application/pdf" + HTML = "text/html" + PUB = "application/x-mspublisher" + EXCEL_MACRO1 = "application/vnd.ms-excel.addin.macroEnabled" + EXCEL_MACRO2 = "application/vnd.ms-excel.sheet.macroEnabled.12" + EXCEL1 = "application/vnd.ms-excel" + EXCEL2 = "application/excel" + DOC = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + XML1 = "application/xml" + XML2 = "text/xml" + ENCRYPTED = "application/encrypted" + PLAIN = "text/plain" + CSV = "text/csv" + PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation" + WORD1 = "application/msword" + WORD2 = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + POWERPOINT = "application/vnd.ms-powerpoint" + OFFICE = "application/vnd.ms-office" + BINARY = "application/x-binary" + MAC1 = "application/x-macbinary" + MAC2 = "application/mac-binary" + MAC3 = "application/x-mach-binary" + COMPRESS1 = "application/x-zip-compressed" + COMPRESS2 = "application/x-compressed" + OUTLOOK = "application/vnd.ms-outlook" + EML = "message/rfc822" + PKCS7 = "application/pkcs7-signature" + XPKCS7 = "application/x-pkcs7-signature" + MIXED = "multipart/mixed" + X_SHELLSCRIPT = "text/x-shellscript" + CRX = "application/x-chrome-extension" + JSON = "application/json" + + @classmethod + def _calculate_from_filename(cls, file_name: str) -> Optional["MimeTypes"]: + if file_name.endswith(".js") or file_name.endswith(".jse"): + mimetype = cls.JAVASCRIPT1 + elif file_name.endswith(".vbs") or file_name.endswith(".vbe"): + mimetype = cls.VB_SCRIPT + elif file_name.endswith(".iqy"): + mimetype = cls.IQY + elif file_name.endswith(".apk"): + mimetype = cls.APK + elif file_name.endswith(".dex"): + mimetype = cls.DEX + elif file_name.endswith(".one"): + mimetype = cls.ONE_NOTE + else: + return None + return mimetype + + @classmethod + def calculate(cls, file_pointer, file_name) -> str: + from magic import from_buffer as magic_from_buffer + + mimetype = None + if file_name: + mimetype = cls._calculate_from_filename(file_name) + + if mimetype is None: + buffer = file_pointer.read() + mimetype = magic_from_buffer(buffer, mime=True) + logger.debug(f"mimetype is {mimetype}") + try: + mimetype = cls(mimetype) + except ValueError: + logger.info( + f"Unable to valid a {cls.__name__} for mimetype {mimetype}" + f" for file {file_name}" + ) + else: + mimetype = mimetype.value + + return mimetype + + +class AnalyzerConfig(PythonConfig): + # generic + type = models.CharField(choices=TypeChoices.choices, null=False, max_length=50) + docker_based = models.BooleanField(null=False, default=False) + maximum_tlp = models.CharField( + null=False, default=TLP.RED, choices=TLP.choices, max_length=50 + ) + python_module = models.ForeignKey( + PythonModule, + on_delete=models.PROTECT, + related_name="%(class)ss", + limit_choices_to={ + "base_path__in": [ + PythonModuleBasePaths.FileAnalyzer.value, + PythonModuleBasePaths.ObservableAnalyzer.value, + ] + }, + ) + # obs + observable_supported = ChoiceArrayField( + models.CharField(null=False, choices=ObservableTypes.choices, max_length=30), + default=list, + blank=True, + ) + + # file + supported_filetypes = ChoiceArrayField( + models.CharField(null=False, max_length=90, choices=MimeTypes.choices), + default=list, + blank=True, + ) + run_hash = models.BooleanField(default=False) + run_hash_type = models.CharField( + blank=True, choices=HashChoices.choices, max_length=10 + ) + not_supported_filetypes = ChoiceArrayField( + models.CharField(null=False, max_length=90, choices=MimeTypes.choices), + default=list, + blank=True, + ) + orgs_configuration = GenericRelation( + "api_app.OrganizationPluginConfiguration", related_name="%(class)s" + ) + + @classmethod + @property + def serializer_class(cls): + from api_app.analyzers_manager.serializers import AnalyzerConfigSerializer + + return AnalyzerConfigSerializer + + def clean_observable_supported(self): + if self.type == TypeChoices.OBSERVABLE and not self.observable_supported: + raise ValidationError( + "You have to specify at least one type of observable supported" + ) + if self.type != TypeChoices.OBSERVABLE and self.observable_supported: + raise ValidationError( + "You can't specify an observable type if you do not support observable" + ) + + def clean_filetypes(self): + if self.type == TypeChoices.FILE: + if self.supported_filetypes and self.not_supported_filetypes: + raise ValidationError( + "Please specify only one between " + "supported_filetypes and not_supported_filetypes" + ) + else: + if self.supported_filetypes or self.not_supported_filetypes: + raise ValidationError( + "You can't specify supported_filetypes or " + "not_supported_filetypes if you do not support files" + ) + + def clean_run_hash_type(self): + if self.run_hash and not self.run_hash_type: + raise ValidationError("run_hash_type must be populated if run_hash is True") + + def clean(self): + super().clean() + self.clean_run_hash_type() + self.clean_observable_supported() + self.clean_filetypes() + + @classmethod + @property + def plugin_type(cls) -> str: + return "1" + + @classmethod + @property + def config_exception(cls): + return AnalyzerConfigurationException diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/abuseipdb.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/abuseipdb.py new file mode 100644 index 0000000..674c92a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/abuseipdb.py @@ -0,0 +1,95 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class AbuseIPDB(ObservableAnalyzer): + url: str = "https://api.abuseipdb.com/api/v2/check" + + _api_key_name: str + max_age: int + max_reports: int + verbose: bool + + def run(self): + headers = {"Key": self._api_key_name, "Accept": "application/json"} + params_ = { + "ipAddress": self.observable_name, + "maxAgeInDays": self.max_age, + "verbose": self.verbose, + } + response = requests.get(self.url, params=params_, headers=headers) + response.raise_for_status() + + result = response.json() + reports = result.get("data", {}).get("reports", []) + mapping = self._get_mapping() + categories_found = {} + # I want to use all the reports to extract the categories numbers + # Afterwards we can prune them if they are too much + for report in reports: + report["categories_human_readable"] = [] + for category in report.get("categories", []): + category_human_readable = mapping.get(category, "unknown category") + report["categories_human_readable"].append(category_human_readable) + if category_human_readable not in categories_found: + categories_found[category_human_readable] = 1 + else: + categories_found[category_human_readable] += 1 + + # adding items to the base result + result["categories_found"] = categories_found + result["permalink"] = f"https://www.abuseipdb.com/check/{self.observable_name}" + + # limiting output result cause it can be really big + if reports: + result["data"]["reports"] = result["data"]["reports"][: self.max_reports] + + return result + + @staticmethod + def _get_mapping(): + mapping = { + 1: "DNS Compromise", + 2: "DNS Poisoning", + 3: "Fraud Orders", + 4: "DDOS Attack", + 5: "FTP Brute-Force", + 6: "Ping of Death", + 7: "Phishing", + 8: "Fraud VOIP", + 9: "Open Proxy", + 10: "Web Spam", + 11: "Email Spam", + 12: "Blog Spam", + 13: "VPN IP", + 14: "Port Scan", + 15: "Hacking", + 16: "SQL Injection", + 17: "Spoofing", + 18: "Brute Force", + 19: "Bad Web Bot", + 20: "Exploited Host", + 21: "Web App Attack", + 22: "SSH", + 23: "IoT Targeted", + } + return mapping + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + {"data": {"reports": [{"categories": [1, 2]}]}}, 200 + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/abusix.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/abusix.py new file mode 100644 index 0000000..440a6bb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/abusix.py @@ -0,0 +1,38 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +import querycontacts + +from api_app.analyzers_manager import classes +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Abusix(classes.ObservableAnalyzer): + def run(self): + result = {} + ip_addr = self.observable_name + cf = querycontacts.ContactFinder() + abuse_contacts = cf.find(ip_addr) + if not abuse_contacts: + abuse_contacts = [] + result["abuse_contacts"] = abuse_contacts + return result + + def update(self) -> bool: + pass + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "querycontacts.ContactFinder.find", + return_value=["network-abuse@google.com"], + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ailtyposquatting.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ailtyposquatting.py new file mode 100644 index 0000000..0b79815 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ailtyposquatting.py @@ -0,0 +1,67 @@ +import logging +import math + +from ail_typo_squatting import typo +from ail_typo_squatting.dns_local import resolving + +from api_app.analyzers_manager import classes +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class AilTypoSquatting(classes.ObservableAnalyzer): + """ + wrapper for https://github.com/typosquatter/ail-typo-squatting + """ + + dns_resolving: bool = False + + def update(self) -> bool: + pass + + def run(self): + response = {} + logger.info( + f"""running AilTypoSquatting on {self.observable_name} + with tlp {self._job.tlp} + and dns resolving {self.dns_resolving}""" + ) + + response["algorithms"] = typo.runAll( + domain=self.observable_name, + limit=math.inf, + formatoutput="text", + pathOutput=None, + ) + if self._job.tlp == self._job.TLP.CLEAR.value and self.dns_resolving: + # for "x.com", response["algorithms"][0]=".com" + # which is not valid for look up + if len(self.observable_name.split(".")[0]) == 1: + logger.info( + f"""running dns resolving on {self.observable_name} + excluding {response['algorithms'][0]}""" + ) + response["dnsResolving"] = resolving.dnsResolving( + resultList=response["algorithms"][1:], + domain=self.observable_name, + pathOutput=None, + ) + else: + response["dnsResolving"] = resolving.dnsResolving( + resultList=response["algorithms"], + domain=self.observable_name, + pathOutput=None, + ) + + return response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch.object(typo, "runAll", return_value=None), + patch.object(resolving, "dnsResolving", return_value=None), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/auth0.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/auth0.py new file mode 100644 index 0000000..0d8f26d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/auth0.py @@ -0,0 +1,39 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Auth0(classes.ObservableAnalyzer): + name: str = "Auth0" + url: str = "https://signals.api.auth0.com/v2.0/ip" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = {"X-Auth-Token": self._api_key_name} + url = f"{self.url}/{self.observable_name}" + response = requests.get(url, headers=headers) + response.raise_for_status() + + json_response = response.json() + return json_response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/bgp_ranking.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/bgp_ranking.py new file mode 100644 index 0000000..5ca8ec8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/bgp_ranking.py @@ -0,0 +1,148 @@ +import json +import logging + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class BGPRanking(classes.ObservableAnalyzer): + """ + wrapper for https://github.com/D4-project/BGP-Ranking + """ + + url: str + timeout: int + period: int # optional + + def update(self) -> bool: + pass + + def run(self): + final_response = {} + + # get ASN from ip + + logger.info(f"Extracting ASN from IP: {self.observable_name}") + response = requests.get( + self.url + "/ipasn_history/?ip=" + self.observable_name, + timeout=self.timeout, + ) + response.raise_for_status() + response = response.json() + asn = response.get("response", {}).popitem()[1].get("asn", None) + if not asn: + raise AnalyzerRunException(f"ASN not found in {response}") + logger.info(f"ASN {asn} extracted from {self.observable_name}") + + # get ASN rank from extracted ASN + logger.info(f"Extracting ASN rank and position from ASN: {asn}") + response = requests.post( + self.url + "/json/asn", + data=json.dumps({"asn": asn}), + timeout=self.timeout, + ) + response.raise_for_status() + response = response.json() + final_response["asn_description"] = response["response"].get( + "asn_description", None + ) + final_response["asn_rank"] = response["response"]["ranking"].get("rank", None) + final_response["asn_position"] = response["response"]["ranking"].get( + "position", None + ) + if final_response["asn_rank"] is None: + raise AnalyzerRunException(f"ASN rank not found in {response}") + + logger.info( + f"""ASN rank: {final_response['asn_rank']}, + position: {final_response['asn_position']}, + from {self.observable_name}""" + ) + + if self.period: + # get ASN history from extracted ASN + logger.info(f"Extracting ASN history for period: {self.period}") + response = requests.post( + self.url + "/json/asn_history", + data=json.dumps({"asn": asn, "period": self.period}), + timeout=self.timeout, + ) + response.raise_for_status() + response = response.json() + final_response["asn_history"] = response["response"].get( + "asn_history", None + ) + if final_response["asn_history"] is None: + raise AnalyzerRunException(f"ASN history not found in {response}") + logger.info( + f"""ASN history: {final_response['asn_history']} + for {self.observable_name}""" + ) + # we are using the ASN in a variable + # initially to avoid repetitive calculations + final_response["asn"] = asn + + return final_response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + { + "meta": {"ip": "143.255.153.0/24"}, + "response": { + "2024-03-07T12:00:00": { + "asn": "264643", + "prefix": "143.255.153.0/24", + "source": "caida", + } + }, + }, + 200, + ), + ), + patch( + "requests.post", + side_effect=[ + MockUpResponse( + { + "meta": {"asn": "5577"}, + "response": { + "asn_description": "ROOT, LU", + "ranking": { + "rank": 0.0004720052083333333, + "position": 7084, + "total_known_asns": 15375, + }, + }, + }, + 200, + ), + MockUpResponse( + { + "meta": {"asn": "5577", "period": 5}, + "response": { + "asn_history": [ + ["2019-11-10", 0.00036458333333333335], + ["2019-11-11", 0.00036168981481481485], + ["2019-11-12", 0.0003761574074074074], + ["2019-11-13", 0.0003530092592592593], + ["2019-11-14", 0.0003559027777777778], + ] + }, + }, + 200, + ), + ], + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/binaryedge.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/binaryedge.py new file mode 100644 index 0000000..5db03d7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/binaryedge.py @@ -0,0 +1,68 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class BinaryEdge(classes.ObservableAnalyzer): + url: str = "https://api.binaryedge.io/v2/query/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.headers = {"X-Key": self._api_key_name} + + def run(self): + results = {} + if self.observable_classification == self.ObservableTypes.IP: + try: + response_recent_ip_info = requests.get( + self.url + "ip/" + self.observable_name, headers=self.headers + ) + response_recent_ip_info.raise_for_status() + + response_query_ip = requests.get( + self.url + "search?query=ip:" + self.observable_name, + headers=self.headers, + ) + response_query_ip.raise_for_status() + + except requests.RequestException as e: + raise AnalyzerRunException(e) + + results = { + "ip_recent_report": response_recent_ip_info.json(), + "ip_query_report": response_query_ip.json(), + } + elif self.observable_classification == self.ObservableTypes.DOMAIN: + try: + response_domain_report = requests.get( + self.url + "domains/subdomain/" + self.observable_name, + headers=self.headers, + ) + results = response_domain_report.json() + except requests.RequestException as e: + raise AnalyzerRunException(e) + return results + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/bitcoinabuse.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/bitcoinabuse.py new file mode 100644 index 0000000..c454c1d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/bitcoinabuse.py @@ -0,0 +1,33 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class BitcoinAbuseAPI(classes.ObservableAnalyzer): + url: str = "https://www.bitcoinabuse.com/api/reports/check" + + _api_key_name: str + + def run(self): + params = {"address": self.observable_name, "api_token": self._api_key_name} + + response = requests.get(self.url, params=params) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/censys.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/censys.py new file mode 100644 index 0000000..fa6bfbf --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/censys.py @@ -0,0 +1,90 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Censys(classes.ObservableAnalyzer): + """ + Censys search analyzer class. Analyzes IP addresses. + Ugraded api endpoint v2 + Please apply secreats using: https://search.censys.io/account/api + """ + + def update(self): + pass + + url = "https://search.censys.io/api/v2" + + censys_analysis: str + _api_id_name: str + _api_secret_name: str + + def run(self): + if self.censys_analysis == "search": + uri = f"/hosts/{self.observable_name}" + else: + raise AnalyzerRunException( + f"not supported observable type {self.observable_classification}." + "Supported is IP" + ) + response = requests.get( + self.url + uri, + auth=(self._api_id_name, self._api_secret_name), + headers={ + "Accept": "application/json", + }, + ) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + response = { + "code": 200, + "status": "OK", + "result": { + "ip": "190.121.56.10", + "services": [], + "location": { + "continent": "South America", + "country": "Chile", + "country_code": "CL", + "city": "Osorno", + "postal_code": "5290000", + "timezone": "America/Santiago", + "province": "Los Lagos Region", + "coordinates": { + "latitude": -40.57395, + "longitude": -73.13348, + }, + }, + "location_updated_at": "2024-01-27T14:52:11.775086600Z", + "autonomous_system": { + "asn": 14117, + "description": "Telefonica del Sur S.A.", + "bgp_prefix": "190.121.56.0/21", + "name": "Telefonica del Sur S.A.", + "country_code": "CL", + }, + "autonomous_system_updated_at": "2024-01-27T14:52:11.775086600Z", + "last_updated_at": "2023-04-30T00:04:14.886Z", + }, + } + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + response, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/checkdmarc.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/checkdmarc.py new file mode 100644 index 0000000..9bb43e3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/checkdmarc.py @@ -0,0 +1,33 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +import subprocess +from shutil import which + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException + + +class CheckDMARC(classes.ObservableAnalyzer): + check_command: str = "checkdmarc" + + def run(self): + if not which(self.check_command): + raise AnalyzerRunException("checkdmarc not installed!") + + process = subprocess.Popen( + [self.check_command, self.observable_name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + process.wait() + stdout, stderr = process.communicate() + + dmarc_info = stdout.decode("utf-8"), stderr + + dmarc_str = dmarc_info[0] + + dmarc_json = json.loads(dmarc_str) + + return dmarc_json diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/checkphish.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/checkphish.py new file mode 100644 index 0000000..dc64fb8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/checkphish.py @@ -0,0 +1,81 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import time + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class CheckPhish(classes.ObservableAnalyzer): + url: str = "https://developers.checkphish.ai/api/neo/scan" + status_url: str = url + "/status" + + polling_tries: int + polling_time: float + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + json_data = { + "apiKey": self._api_key_name, + "urlInfo": {"url": self.observable_name}, + } + + response = requests.post(CheckPhish.url, json=json_data) + response.raise_for_status() + + job_id = response.json().get("jobID") + if job_id is None: + raise AnalyzerRunException( + "Job creation confirmation not received from CheckPhish." + ) + + return self.__poll_analysis_status(job_id) + + def __poll_analysis_status(self, job_id): + json_data = { + "apiKey": self._api_key_name, + "jobID": job_id, # Assumption: jobID corresponds to an actual job. + # This is always the case when this function is called + # in the "run" function. + "insights": True, # setting "insights" to True adds "screenshot_path" + # and "resolved" fields to the response + } + for chance in range(self.polling_tries): + if chance != 0: + time.sleep(self.polling_time) + response = requests.post(CheckPhish.status_url, json=json_data) + response.raise_for_status() + result = response.json() + status_json = result.get("status", "") + error = result.get("error", False) + if status_json is None: + raise AnalyzerRunException(f"Job {job_id} not found.") + if error: + raise AnalyzerRunException(f"Analysis error for job_id {job_id}") + if status_json == "DONE": + return result + raise AnalyzerRunException(f'Job "{job_id}" status retrieval failed.') + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + side_effect=[ + MockUpResponse({"jobID": "sample job ID"}, 200), + MockUpResponse({"status": "DONE"}, 200), + ], + ), + ), + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/circl_pdns.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/circl_pdns.py new file mode 100644 index 0000000..789200b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/circl_pdns.py @@ -0,0 +1,66 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import datetime +import typing +from typing import Dict +from urllib.parse import urlparse + +import pypdns + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from certego_saas.apps.user.models import User +from tests.mock_utils import MockResponseNoOp, if_mock_connections, patch + + +class CIRCL_PDNS(classes.ObservableAnalyzer): + _pdns_credentials: str + url = "https://www.circl.lu/pdns/query" + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.domain = self.observable_name + if self.observable_classification == self.ObservableTypes.URL: + self.domain = urlparse(self.observable_name).hostname + # You should save CIRCL credentials with this template: "|" + self.split_credentials = self._pdns_credentials.split("|") + if len(self.split_credentials) != 2: + raise AnalyzerRunException( + "CIRCL credentials not properly configured." + "Template to use: '|'" + ) + + def run(self): + user, pwd = self.split_credentials + pdns = pypdns.PyPDNS(url=self.url, basic_auth=(user, pwd)) + + try: + result = pdns.query(self.domain, timeout=5) + except pypdns.errors.UnauthorizedError as e: + raise AnalyzerRunException( + f"Credentials are not valid: UnauthorizedError: {e}" + ) + + for result_item in result: + keys_to_decode = ["time_first", "time_last"] + for key_to_decode in keys_to_decode: + time_extracted = result_item.get(key_to_decode, None) + if time_extracted and isinstance(time_extracted, datetime.datetime): + result_item[key_to_decode] = time_extracted.strftime( + "%Y-%m-%d %H:%M:%S" + ) + + return result + + def _get_health_check_url(self, user: User = None) -> typing.Optional[str]: + return self.url + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch("pypdns.PyPDNS", return_value=MockResponseNoOp({}, 200)), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/circl_pssl.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/circl_pssl.py new file mode 100644 index 0000000..0813a68 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/circl_pssl.py @@ -0,0 +1,66 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import typing +from typing import Dict + +import pypssl + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerConfigurationException +from certego_saas.apps.user.models import User +from tests.mock_utils import MockResponseNoOp, if_mock_connections, patch + + +class CIRCL_PSSL(classes.ObservableAnalyzer): + _pdns_credentials: str + url = "https://www.circl.lu/" + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.__split_credentials = self._pdns_credentials.split("|") + if len(self.__split_credentials) != 2: + raise AnalyzerConfigurationException( + "CIRCL credentials not properly configured." + "Template to use: '|'" + ) + + def run(self): + user = self.__split_credentials[0] + pwd = self.__split_credentials[1] + + pssl = pypssl.PyPSSL(base_url=self.url, basic_auth=(user, pwd)) + + result = pssl.query(self.observable_name, timeout=5) + + certificates = [] + if result.get(self.observable_name, {}): + certificates = list( + result.get(self.observable_name).get("certificates", []) + ) + + parsed_result = {"ip": self.observable_name, "certificates": []} + for cert in certificates: + subject = ( + result.get(self.observable_name) + .get("subjects", {}) + .get(cert, {}) + .get("values", []) + ) + if subject: + parsed_result["certificates"].append( + {"fingerprint": cert, "subject": subject[0]} + ) + + return parsed_result + + def _get_health_check_url(self, user: User = None) -> typing.Optional[str]: + return self.url + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch("pypssl.PyPSSL", return_value=MockResponseNoOp({}, 200)), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/crowdsec.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/crowdsec.py new file mode 100644 index 0000000..20f9c08 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/crowdsec.py @@ -0,0 +1,55 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests +from django.conf import settings + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Crowdsec(ObservableAnalyzer): + _api_key_name: str + url: str = "https://cti.api.crowdsec.net" + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = { + "x-api-key": self._api_key_name, + "User-Agent": f"crowdsec-intelowl/{settings.VERSION}", + } + url = f"{self.url}/v2/smoke/{self.observable_name}" + response = requests.get(url, headers=headers) + if response.status_code == 404: + result = {"not_found": True} + else: + response.raise_for_status() + result = response.json() + result["link"] = f"https://app.crowdsec.net/cti/{self.observable_name}" + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + { + "behaviors": [ + { + "name": "http:exploit", + "label": "HTTP Exploit", + "description": "bla bla", + } + ] + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/crxcavator.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/crxcavator.py new file mode 100644 index 0000000..7c4e057 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/crxcavator.py @@ -0,0 +1,39 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class CRXcavator(classes.ObservableAnalyzer): + name: str = "CRXcavator" + url: str = "https://api.crxcavator.io/v1/report/" + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + try: + response = requests.get(self.url + self.observable_name) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cyberchef.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cyberchef.py new file mode 100644 index 0000000..c744e45 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cyberchef.py @@ -0,0 +1,63 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import json +from typing import Dict + +import requests +from django.conf import settings + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + + +class CyberChef(ObservableAnalyzer, DockerBasedAnalyzer): + name: str = "CyberChefServer" + url: str = "http://cyberchef-server:3000/bake" + config_filename: str = "cyberchef_recipes.json" + + recipe_name: str + recipe_code: list + output_type: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self.recipe_name: + try: + try: + with open( + f"{settings.PROJECT_LOCATION}/configuration/" + f"{self.config_filename}", + "r", + ) as recipes: + parsed_recipes = json.load(recipes) + self.recipe = parsed_recipes[self.recipe_name] + except FileNotFoundError: + raise AnalyzerRunException( + f"Could not open configuration file {self.config_filename}" + ) + except json.JSONDecodeError: + raise AnalyzerRunException( + f"Could not parse the configuration file. Please check " + f"{self.config_filename}" + ) + + except KeyError: + raise AnalyzerRunException( + f"Unknown predefined recipe: {self.recipe_name}" + ) + else: + self.recipe = self.recipe_code + + def run(self): + try: + request_payload = {"input": self.observable_name, "recipe": self.recipe} + if self.output_type: + request_payload["outputType"] = self.output_type + response = requests.post(self.url, json=request_payload) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + result = response.json() + + return result diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cycat.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cycat.py new file mode 100644 index 0000000..d1ff11b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cycat.py @@ -0,0 +1,100 @@ +import logging +import re + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class CyCat(classes.ObservableAnalyzer): + """ + This analyzer is a wrapper for cycat api. + """ + + def update(self) -> bool: + pass + + url: str = "https://api.cycat.org" + + def uuid_lookup(self, uuid: str): + logger.info( + f"performing lookup on uuid: {uuid}, observable: {self.observable_name}" + ) + response = requests.get( + self.url + "/lookup/" + uuid, + headers={"accept": "application/json"}, + ) + response.raise_for_status() + return response.json() + + def run(self): + final_response = {} + uuid_pattern = re.compile( + r"\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b", + re.IGNORECASE, + ) + if uuid_pattern.match(self.observable_name): + final_response = self.uuid_lookup(self.observable_name) + + else: + response = requests.get( + self.url + "/search/" + self.observable_name, + headers={"accept": "application/json"}, + ) + response.raise_for_status() + response = response.json() + for uuid in response: + final_response[uuid] = self.uuid_lookup(uuid) + return final_response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + { + "description": """Detects Execution via + SyncInvoke in CL_Invocation.ps1 module""", + "raw": """ + author: oscd.community, Natalia Shornikova + date: 2020/10/14 + description: Detects Execution via + SyncInvoke in CL_Invocation.ps1 module + detection: + condition: selection + selection: + EventID: 4104 + ScriptBlockText|contains|all: + - CL_Invocation.ps1 + - SyncInvoke + falsepositives: + - Unknown + id: 4cd29327-685a-460e-9dac-c3ab96e549dc + level: high + logsource: + product: windows + service: powershell + modified: 2021/05/21 + references: + - https://twitter.com/bohops/status/948061991012327424 + status: experimental + tags: + - attack.defense_evasion + - attack.t1216 + title: Execution via CL_Invocation.ps1 + """, + "sigma:id": "4cd29327-685a-460e-9dac-c3ab96e549dc", + "title": "Execution via CL_Invocation.ps1", + "_cycat_type": "Item", + }, + 200, + ), + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cymru.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cymru.py new file mode 100644 index 0000000..5d7fcd8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/cymru.py @@ -0,0 +1,49 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import socket + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException + +logger = logging.getLogger(__name__) + + +class Cymru(ObservableAnalyzer): + def run(self): + results = {} + if self.observable_classification != self.ObservableTypes.HASH: + raise AnalyzerRunException( + f"observable type {self.observable_classification} not supported" + ) + + hash_length = len(self.observable_name) + if hash_length == 64: + raise AnalyzerRunException("sha256 are not supported by the service") + + results["found"] = False + # reference: https://team-cymru.com/community-services/mhr/ + # if the resolution works, this means that the file is reported + # as malware by Cymru + domains = None + try: + query_to_perform = f"{self.observable_name}.malware.hash.cymru.com" + domains = socket.gethostbyaddr(query_to_perform) + except (socket.gaierror, socket.herror): + logger.info(f"observable {self.observable_name} not found in HMR DB") + except socket.timeout: + message = f"request for {self.observable_name} in HMR DB triggered timeout" + logger.warning(message) + self.report.errors.append(message) + results["timeout"] = True + except Exception as e: + logger.exception(e) + self.report.errors.append(e) + results["unexpected_error"] = True + + if domains: + results["found"] = True + results["resolution_data"] = domains[2] + + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dehashed.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dehashed.py new file mode 100644 index 0000000..d26ff96 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dehashed.py @@ -0,0 +1,117 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import base64 +import logging +import re + +import requests +from requests.structures import CaseInsensitiveDict + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.constants import ObservableTypes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class DehashedSearch(ObservableAnalyzer): + """ + Search a keyword on dehashed.com's search API. + - API key is mandatory for dehased.com's API. + """ + + url: str = "https://api.dehashed.com/" + size: int + pages: int + operator: str + _api_key_name: str + + def run(self): + # try to identify search operator + self.__identify_search_operator() + if self.operator in ["name", "address"] and " " in self.observable_name: + # this is to allow to do "match_phrase" queries + # ex: "John Smith" would match the entire phrase + # ex: John Smith would match also John alone + # same for the addresses + cleaned_observable_name = f'"{self.observable_name}"' + else: + cleaned_observable_name = self.observable_name + value = f"{self.operator}:{cleaned_observable_name}" + + # execute searches + entries = self.__search(value) + + logger.info( + f"result for observable {self.observable_name} is: query:" + f" {value}, pages {self.pages}, operator: {self.operator}" + ) + + return { + "query_value": value, + "pages_queried": self.pages, + "operator": self.operator, + "entries": entries, + } + + def __identify_search_operator(self): + if self.observable_classification == ObservableTypes.IP: + self.operator = "ip_address" + elif self.observable_classification == ObservableTypes.DOMAIN: + self.operator = "domain" + elif self.observable_classification == ObservableTypes.URL: + self.operator = "domain" + elif self.observable_classification == ObservableTypes.GENERIC: + if re.match(r"^[\w\.\+\-]+\@[\w]+\.[a-z]{2,3}$", self.observable_name): + self.operator = "email" + # order matters! it's important "address" is placed before "phone" + elif " " in self.observable_name and re.match( + r"\d.*[a-zA-Z]|[a-zA-Z].*\d", self.observable_name + ): + self.operator = "address" + elif re.match(r"\+?\d+", self.observable_name): + self.operator = "phone" + elif " " in self.observable_name: + self.operator = "name" + + def __search(self, value: str) -> list: + # the API uses basic auth so we need to base64 encode the auth payload + auth_b64 = base64.b64encode(self._api_key_name.encode()).decode() + # construct headers + headers = CaseInsensitiveDict( + { + "Accept": "application/json", + "Authorization": f"Basic {auth_b64}", + "User-Agent": "IntelOwl", + } + ) + url = f"{self.url}search?query={value}&size={self.size}" + + total_entries = [] + for page_no in range(1, self.pages + 1): + logger.info( + f"{self.__repr__()} -> fetching search results for page #{page_no}" + ) + resp = requests.get(f"{url}&page={page_no}", headers=headers) + resp.raise_for_status() + entries_fetched = resp.json().get("entries", None) + if not entries_fetched: + entries_fetched = [] + else: + total_entries.extend(entries_fetched) + logger.info(f"{self.__repr__()} -> got {len(entries_fetched)} entries") + + return total_entries + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"entries": [{"id": "test"}]}, 200), + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/cloudflare_malicious_detector.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/cloudflare_malicious_detector.py new file mode 100644 index 0000000..aad873b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/cloudflare_malicious_detector.py @@ -0,0 +1,65 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Check if the domains is reported as malicious in CloudFlare database""" + +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import malicious_detector_response + + +class CloudFlareMaliciousDetector(classes.ObservableAnalyzer): + """Resolve a DNS query with CloudFlare security endpoint, + if response is 0.0.0.0 the domain in DNS query is malicious. + """ + + def run(self): + try: + is_malicious = False + observable = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + + params = { + "name": observable, + "type": "A", + } + headers = {"accept": "application/dns-json"} + response = requests.get( + "https://security.cloudflare-dns.com/dns-query", + params=params, + headers=headers, + ) + response.raise_for_status() + response_dict = response.json() + + response_answer = response_dict.get("Answer", []) + if response_answer: + resolution = response_answer[0].get("data", "") + # CloudFlare answers with 0.0.0.0 if the domain is known as malicious + if resolution == "0.0.0.0": + is_malicious = True + + except requests.exceptions.RequestException: + raise AnalyzerRunException("Connection to CloudFlare failed") + + return malicious_detector_response(self.observable_name, is_malicious) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"Answer": [{"data": "0.0.0.0"}]}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/dns0_eu_malicious_detector.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/dns0_eu_malicious_detector.py new file mode 100644 index 0000000..a9e3627 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/dns0_eu_malicious_detector.py @@ -0,0 +1,78 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Check if the domains is reported as malicious in DNS0.eu database""" + +import logging +from ipaddress import AddressValueError, IPv4Address +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import malicious_detector_response + +logger = logging.getLogger(__name__) + + +class DNS0EUMaliciousDetector(classes.ObservableAnalyzer): + class NotADomain(Exception): + pass + + def run(self): + observable = self.observable_name + is_malicious = False + try: + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + try: + IPv4Address(observable) + except AddressValueError: + pass + else: + raise self.NotADomain() + + params = { + "name": observable, + "type": "A", + } + headers = {"accept": "application/dns-json"} + response = requests.get( + "https://zero.dns0.eu", + params=params, + headers=headers, + ) + response.raise_for_status() + response_dict = response.json() + + response_answer = response_dict.get("Authority", []) + if response_answer: + resolution = response_answer[0].get("data", "") + # CloudFlare answers with 0.0.0.0 if the domain is known as malicious + if "negative-caching.dns0.eu" in resolution: + is_malicious = True + + except requests.exceptions.RequestException: + raise AnalyzerRunException("Connection to DNS0 failed") + except self.NotADomain: + logger.info(f"not analyzing {observable} because not a domain") + + return malicious_detector_response(self.observable_name, is_malicious) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + {"Answer": [{"data": "negative-caching.dns0.eu"}]}, 200 + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/google_webrisk.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/google_webrisk.py new file mode 100644 index 0000000..d5e4bf2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/google_webrisk.py @@ -0,0 +1,103 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Check if the domains is reported as malicious by WebRisk Cloud API""" +import logging + +from google.cloud.webrisk_v1.services.web_risk_service import WebRiskServiceClient +from google.cloud.webrisk_v1.types import ThreatType +from google.oauth2 import service_account + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from api_app.analyzers_manager.observable_analyzers.dns.dns_responses import ( + malicious_detector_response, +) +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class WebRisk(classes.ObservableAnalyzer): + """Check if observable analyzed is marked as malicious by Google WebRisk API + + Get these secrets from a Service Account valid file. + Example: + { + "type": "service_account", + "project_id": "test", + "private_key_id": "34543543543534", + "private_key": "test", + "client_email": "test@test.iam.gserviceaccount.com", + "client_id": "363646436363463663634", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": + "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": + "https://www.googleapis.com/robot/v1/metadata/x509/somedomain" + } + """ + + _service_account_json: dict + + def run(self): + if ( + self.observable_classification == self.ObservableTypes.URL + and not self.observable_name.startswith("http") + ): + raise AnalyzerRunException( + f"{self.observable_name} not supported " + "because it does not start with http" + ) + + credentials = service_account.Credentials.from_service_account_info( + self._service_account_json + ) + + web_risk_client = WebRiskServiceClient(credentials=credentials) + # threat types + # MALWARE = 1 + # SOCIAL_ENGINEERING = 2 + # THREAT_TYPE_UNSPECIFIED = 0 should not be used + # UNWANTED_SOFTWARE = 3 + threat_types = [ThreatType(1), ThreatType(2), ThreatType(3)] + response = web_risk_client.search_uris( + uri=self.observable_name, threat_types=threat_types, timeout=5 + ) + threats_found = response.threat + # ThreatUri object + logger.debug(f"threat founds {threats_found}") + + threat_types = threats_found.threat_types + + malicious = bool(threat_types) + web_risk_result = malicious_detector_response(self.observable_name, malicious) + # append extra data + if malicious: + threats_list = [] + if 1 in threat_types: + threats_list.append("MALWARE") + if 2 in threat_types: + threats_list.append("SOCIAL_ENGINEERING") + if 3 in threat_types: + threats_list.append("UNWANTED_SOFTWARE") + web_risk_result["threats"] = threats_list + return web_risk_result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "api_app.analyzers_manager.observable_analyzers.dns." + "dns_malicious_detectors.google_webrisk.WebRiskServiceClient" + ), + patch.object( + service_account.Credentials, + "from_service_account_info", + return_value={}, + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/googlesf.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/googlesf.py new file mode 100644 index 0000000..a6b3a89 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/googlesf.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Check if the domains is reported as malicious for GoogleSafeBrowsing""" +from typing import Dict, List + +import pysafebrowsing + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import if_mock_connections, patch + +from ..dns_responses import malicious_detector_response + + +class MockUpSafeBrowsing: + @staticmethod + def lookup_urls(urls: List[str]) -> Dict: + return { + url: { + "malicious": True, + "cache": "test", + "threats": "test", + "platforms": "test", + } + for url in urls + } + + +class GoogleSF(classes.ObservableAnalyzer): + """Check if observable analyzed is marked as malicious for Google SafeBrowsing""" + + _api_key_name: str + + def run(self): + sb_instance = pysafebrowsing.SafeBrowsing(self._api_key_name) + response = sb_instance.lookup_urls([self.observable_name]) + if self.observable_name in response and isinstance( + response[self.observable_name], dict + ): + result = response[self.observable_name] + else: + raise AnalyzerRunException(f"result not expected: {response}") + + malicious = result["malicious"] + googlesb_result = malicious_detector_response(self.observable_name, malicious) + # append google extra data + if malicious: + googlesb_result["cache"] = result["cache"] + googlesb_result["threats"] = result["threats"] + googlesb_result["platforms"] = result["platforms"] + return googlesb_result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch("pysafebrowsing.SafeBrowsing", return_value=MockUpSafeBrowsing()), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/quad9_malicious_detector.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/quad9_malicious_detector.py new file mode 100644 index 0000000..bb94d9a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/quad9_malicious_detector.py @@ -0,0 +1,111 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Check if the domains is reported as malicious in Quad9 database""" + +import logging +from typing import Tuple +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import malicious_detector_response + +logger = logging.getLogger(__name__) + + +class Quad9MaliciousDetector(classes.ObservableAnalyzer): + """Check if a domain is malicious by Quad9 public resolver. + Quad9 does not answer in the case a malicious domain is queried. + However, we need to perform another check to understand if that domain was blocked + by the resolver or if it just does not exist. + So we perform one request to Quad9 and another one to Google. + In the case of empty response from Quad9 and a non-empty response from Google, + we can guess that the domain was in the Quad9 blacklist. + """ + + HEADERS = {"Accept": "application/dns-json"} + QUAD9_URL = "https://dns.quad9.net:5053/dns-query" + GOOGLE_URL = "https://dns.google.com/resolve" + + def update(self) -> bool: + pass + + def run(self): + observable = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + + quad9_answer, timeout = self._quad9_dns_query(observable) + # if Quad9 has not an answer the site could be malicious + if not quad9_answer: + # Google dns request + google_answer = self._google_dns_query(observable) + # if Google response, Quad9 marked the site as malicious, + # elsewhere the site does not exist + if google_answer: + return malicious_detector_response(self.observable_name, True) + + return malicious_detector_response(self.observable_name, False, timeout) + + def _quad9_dns_query(self, observable) -> Tuple[bool, bool]: + """Perform a DNS query with Quad9 service, return True if Quad9 answer the + DNS query with a non-empty response. + + :param observable: domain to resolve + :type observable: str + :return: True in case of answer for the DNS query else False. + :rtype: bool + """ + answer_found = False + timeout = False + params = {"name": observable} + + quad9_response = requests.get( + self.QUAD9_URL, headers=self.HEADERS, params=params + ) + if quad9_response.status_code == 503: + msg = ( + "503 status code! " + "It may be normal for this service to" + " happen from time to time" + ) + logger.info(msg) + self.report.errors.append(msg) + timeout = True + return answer_found, timeout + quad9_response.raise_for_status() + answer_found = bool(quad9_response.json().get("Answer", None)) + + return answer_found, timeout + + def _google_dns_query(self, observable) -> bool: + """Perform a DNS query with Google service, return True if Google answer the + DNS query. + + :param observable: domain to resolve + :type observable: str + :return: True in case of answer for the DNS query else False. + :rtype: bool + """ + params = {"name": observable} + google_response = requests.get(self.GOOGLE_URL, params=params) + google_response.raise_for_status() + + return bool(google_response.json().get("Answer", None)) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"Answer": False}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/classic_dns_resolver.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/classic_dns_resolver.py new file mode 100644 index 0000000..cd817be --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/classic_dns_resolver.py @@ -0,0 +1,94 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Default DNS resolutions""" + +import ipaddress +import logging +import socket +from urllib.parse import urlparse + +import dns.resolver + +from api_app.analyzers_manager import classes + +from ..dns_responses import dns_resolver_response + +logger = logging.getLogger(__name__) + + +class ClassicDNSResolver(classes.ObservableAnalyzer): + """Resolve a DNS query with Default resolver""" + + query_type: str + + def run(self): + resolutions = [] + timeout = False + if self.observable_classification == self.ObservableTypes.IP: + try: + ipaddress.ip_address(self.observable_name) + hostname, alias, _ = socket.gethostbyaddr(self.observable_name) + if alias: + resolutions.extend(alias) + if hostname: + resolutions.append(hostname) + except (socket.gaierror, socket.herror): + logger.info(f"No resolution for ip {self.observable_name}") + self.report.errors.append( + f"No resolution for ip {self.observable_name}" + ) + resolutions = [] + except socket.timeout: + message = ( + f"request for {self.observable_name} for classic" + " DNS triggered timeout" + ) + logger.warning(message) + self.report.errors.append(message) + timeout = True + + elif self.observable_classification in [ + self.ObservableTypes.DOMAIN, + self.ObservableTypes.URL, + ]: + observable = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + + try: + dns_resolutions = dns.resolver.resolve(observable, self.query_type) + for resolution in dns_resolutions: + element = { + "TTL": dns_resolutions.rrset.ttl, + "data": resolution.to_text(), + "name": dns_resolutions.qname.to_text(), + "type": dns_resolutions.rdtype, + } + resolutions.append(element) + except ( + dns.resolver.NXDOMAIN, + dns.resolver.NoAnswer, + dns.resolver.NoNameservers, + ): + logger.info( + "No resolution for " + f"{self.observable_classification} {self.observable_name}" + ) + except dns.resolver.LifetimeTimeout as e: + logger.warning( + "No resolution for " + f"{self.observable_classification} {self.observable_name}." + f"Reason {e}", + stack_info=True, + ) + self.report.errors.append(str(e)) + timeout = True + + return dns_resolver_response(self.observable_name, resolutions, timeout) + + @classmethod + def _monkeypatch(cls): + patches = [] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/cloudflare_dns_resolver.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/cloudflare_dns_resolver.py new file mode 100644 index 0000000..63cf028 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/cloudflare_dns_resolver.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""CloudFlare DNS resolutions""" + +import logging +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import dns_resolver_response + +logger = logging.getLogger(__name__) + + +class CloudFlareDNSResolver(classes.ObservableAnalyzer): + """Resolve a DNS query with CloudFlare""" + + query_type: str + + def run(self): + try: + observable = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + + params = { + "name": observable, + "type": self.query_type, + } + headers = {"accept": "application/dns-json"} + response = requests.get( + "https://cloudflare-dns.com/dns-query", params=params, headers=headers + ) + response.raise_for_status() + response_dict = response.json() + + resolutions = response_dict.get("Answer", None) + + except requests.exceptions.RequestException as error: + raise AnalyzerRunException( + f"An error occurred during the connection to CloudFlare: {error}" + ) + return dns_resolver_response(self.observable_name, resolutions) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"Answer": ["test1", "test2"]}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/dns0_eu_resolver.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/dns0_eu_resolver.py new file mode 100644 index 0000000..4439d92 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/dns0_eu_resolver.py @@ -0,0 +1,67 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from ipaddress import AddressValueError, IPv4Address +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import dns_resolver_response + +logger = logging.getLogger(__name__) + + +class DNS0EUResolver(classes.ObservableAnalyzer): + """Resolve a DNS query with DNS0.eu""" + + class NotADomain(Exception): + pass + + query_type: str + + def run(self): + observable = self.observable_name + resolutions = None + try: + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + try: + IPv4Address(observable) + except AddressValueError: + pass + else: + raise self.NotADomain() + + headers = {"Accept": "application/dns-json"} + url = "https://dns0.eu" + params = {"name": observable, "type": self.query_type} + + response = requests.get(url, headers=headers, params=params) + response.raise_for_status() + resolutions = response.json().get("Answer", []) + except requests.RequestException: + raise AnalyzerRunException( + "an error occurred during the connection to DNS0" + ) + except self.NotADomain: + logger.info(f"not analyzing {observable} because not a domain") + + return dns_resolver_response(self.observable_name, resolutions) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"Answer": ["test1", "test2"]}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/google_dns_resolver.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/google_dns_resolver.py new file mode 100644 index 0000000..0987088 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/google_dns_resolver.py @@ -0,0 +1,57 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Google DNS resolutions""" + +import logging +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import dns_resolver_response + +logger = logging.getLogger(__name__) + + +class GoogleDNSResolver(classes.ObservableAnalyzer): + """Resolve a DNS query with Google""" + + query_type: str + + def run(self): + try: + observable = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable = urlparse(self.observable_name).hostname + + params = { + "name": observable, + "type": self.query_type, + } + response = requests.get("https://dns.google.com/resolve", params=params) + response.raise_for_status() + data = response.json() + resolutions = data.get("Answer", None) + except requests.exceptions.RequestException: + raise AnalyzerRunException( + "an error occurred during the connection to Google" + ) + + return dns_resolver_response(self.observable_name, resolutions) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"Answer": ["test1", "test2"]}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/quad9_dns_resolver.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/quad9_dns_resolver.py new file mode 100644 index 0000000..5e4685a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/quad9_dns_resolver.py @@ -0,0 +1,53 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""Quad9 DNS resolutions""" + +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ..dns_responses import dns_resolver_response + + +class Quad9DNSResolver(classes.ObservableAnalyzer): + """Resolve a DNS query with Quad9""" + + query_type: str + + def run(self): + try: + observable = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == "url": + observable = urlparse(self.observable_name).hostname + + headers = {"Accept": "application/dns-json"} + url = "https://dns.quad9.net:5053/dns-query" + params = {"name": observable, "type": self.query_type} + + quad9_response = requests.get(url, headers=headers, params=params) + quad9_response.raise_for_status() + resolutions = quad9_response.json().get("Answer", []) + except requests.RequestException: + raise AnalyzerRunException( + "an error occurred during the connection to Quad9" + ) + + return dns_resolver_response(self.observable_name, resolutions) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"Answer": ["test1", "test2"]}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_responses.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_responses.py new file mode 100644 index 0000000..a803475 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dns/dns_responses.py @@ -0,0 +1,54 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +"""These are the default responses""" + + +def malicious_detector_response( + observable: str, malicious: bool, timeout: bool = False +) -> dict: + """Standard response for malicious detector analyzers + + :param observable: observable analyzed + :type observable: str + :param malicious: tell if the observable is reported as malicious from analyzer + :type malicious: bool + :param timeout: set if the DNS query timed-out + :type timeout bool + :return: + :rtype: dict + """ + + report = {"observable": observable, "malicious": malicious} + + if timeout: + report["timeout"] = True + + return report + + +def dns_resolver_response( + observable: str, resolutions: list = None, timeout: bool = False +) -> dict: + """Standard response for DNS resolver analyzers + + :param observable: observable analyzed + :type observable: str + :param resolutions: list of DNS resolutions, it is empty in case of no resolutions, + default to None + :type resolutions: list, optional + :param timeout: set if the DNS query timed-out + :type timeout bool + :return: + :rtype: dict + """ + + if not resolutions: + resolutions = [] + + report = {"observable": observable, "resolutions": resolutions} + + if timeout: + report["timeout"] = True + + return report diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dnsdb.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dnsdb.py new file mode 100644 index 0000000..489947b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dnsdb.py @@ -0,0 +1,319 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +from typing import Dict, Optional +from urllib.parse import urlparse + +import dateparser +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from certego_saas.apps.user.models import User +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +_query_types = [ + "domain", + "rrname-wildcard-left", + "rrname-wildcard-right", + "names", + "rdata-wildcard-left", + "rdata-wildcard-right", +] +# empty means all rrtypes +# ANY-DNSSEC is an API-specific rrtype that represent all DNSSEC rrtypes +_supported_rrtype = [ + "", + "A", + "AAAA", + "ALIAS", + "CNAME", + "MX", + "NS", + "PTR", + "SOA", + "SRV", + "TXT", + "ANY-DNSSEC", +] +_supported_api_version = [1, 2] + + +class DNSdb(classes.ObservableAnalyzer): + """Farsight passive DNS API + + Support different server. + Support version 1 and 2. + Allow filter on rrtype, count value and timestamp on time fields. + Allow different query types: normal, with left or right wildcard and nameserver. + """ + + _api_key_name: str + server: str + api_version: int + rrtype: str + query_type: str + limit: int + time: dict + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + # API settings + self._time_first_before = self.time.get("first_before", "") + self._time_first_after = self.time.get("first_after", "") + self._time_last_before = self.time.get("last_before", "") + self._time_last_after = self.time.get("last_after", "") + self.no_results_found = False + + def run(self): + # validate params + self._validate_params() + + # generate request parts + headers = self._create_headers() + url = self._create_url() + params = self._create_params() + + # perform request + response = requests.get(url, params=params, headers=headers) + # for API v1, 404 means no results found + if self.api_version == 1 and response.status_code == 404: + self.no_results_found = True + else: + response.raise_for_status() + + # validate output + return self._parse_result(response.text) + + def _validate_params(self): + """Raise an AnalyzerRunException if some params are not valid""" + + if self.api_version not in _supported_api_version: + raise AnalyzerRunException( + f"{self.api_version} not supported version," + f"available versions: {_supported_api_version}" + ) + + if str(self.rrtype) not in _supported_rrtype: + raise AnalyzerRunException( + f"{self.rrtype} is not a valid rrtype: {_supported_rrtype}" + ) + + if self.query_type and self.query_type not in _query_types: + raise AnalyzerRunException( + f"{self.query_type} not in available query types" + ) + + if not isinstance(self.limit, int): + raise AnalyzerRunException( + f"limit: {self.limit} ({type(self.limit)}) must be a integer" + ) + + @staticmethod + def convert_date_type(date_string): + """Convert date into timestamp + + :param date_string: date to be converted into timestamp + :type date_string: str + :return: date timestamp + :rtype: int + """ + try: + return int(dateparser.parse(date_string).timestamp()) + except ValueError: + error_message = f"{date_string} cannot be converted to a valid datetime" + except TypeError: + error_message = ( + f"{type(date_string)} is not a string and cannot be " + f"converted to a datetime " + ) + except Exception: + error_message = ( + f"{date_string} with type: {type(date_string)}," + f"something wrong happened during conversion to datetime" + ) + + raise AnalyzerRunException(error_message) + + def _create_headers(self): + """Generate headers for the API request + + :return: headers + :rtype: dict + """ + if self.api_version == 1: + header_application_type = "application/json" + elif self.api_version == 2: + header_application_type = "application/x-ndjson" + else: + raise AnalyzerRunException( + f"{self.api_version} not in supported versions list: " + f"{_supported_api_version}" + ) + + return {"Accept": header_application_type, "X-API-Key": self._api_key_name} + + def _get_version_endpoint(self, api_version: int): + if api_version == 1: + return "" + elif api_version == 2: + return "/dnsdb/v2" + else: + raise AnalyzerRunException( + f"{api_version} not in supported versions list: " + f"{_supported_api_version}" + ) + + def _create_url(self): + """Generate API url + + :return: API url + :rtype: str + """ + api_version = self._get_version_endpoint(self.api_version) + observable_to_check = self.observable_name + # for URLs we are checking the relative domain + if self.observable_classification == self.ObservableTypes.URL: + observable_to_check = urlparse(self.observable_name).hostname + + if self.observable_classification == self.ObservableTypes.IP: + endpoint = "rdata/ip" + elif self.observable_classification in [ + self.ObservableTypes.DOMAIN, + self.ObservableTypes.URL, + ]: + if self.query_type == "domain": + endpoint = "rrset/name" + elif self.query_type == "rrname-wildcard-left": + endpoint = "rrset/name" + observable_to_check = "*." + observable_to_check + elif self.query_type == "rrname-wildcard-right": + endpoint = "rrset/name" + observable_to_check += ".*" + elif self.query_type == "names": + endpoint = "rdata/name" + elif self.query_type == "rdata-wildcard-left": + endpoint = "rdata/name" + observable_to_check = "*." + observable_to_check + elif self.query_type == "rdata-wildcard-right": + endpoint = "rdata/name" + observable_to_check += ".*" + else: + raise AnalyzerRunException(f"{self.query_type} not supported") + else: + raise AnalyzerRunException( + f"{self.observable_classification} not supported" + ) + + return ( + f"https://{self.server}{api_version}/lookup/{endpoint}" + f"/{observable_to_check}/{self.rrtype}" + ) + + def _create_params(self): + """Generate API request params. + There are filters on time fields and results number. + + :return params: data filters + :rtype params: dict + """ + time_first_before = None + if self._time_first_before: + time_first_before = self.convert_date_type(self._time_first_before) + + time_first_after = None + if self._time_first_after: + time_first_after = self.convert_date_type(self._time_first_after) + + time_last_before = None + if self._time_last_before: + time_last_before = self.convert_date_type(self._time_last_before) + + time_last_after = None + if self._time_last_after: + time_last_after = self.convert_date_type(self._time_last_after) + + params = {"limit": self.limit} + if time_first_before: + params["time_first_before"] = time_first_before + if time_first_after: + params["time_first_after"] = time_first_after + if time_last_before: + params["time_last_before"] = time_last_before + if time_last_after: + params["time_last_after"] = time_last_after + + return params + + def _parse_result(self, result_text): + """Extract data from Farsight response and create a dict with same fields. + Different API version have different format, create same dict structure from + different responses. + + :param result_text: response from Farsight API + :type result_text: str + :return json_extracted_results: Data received from Farsight + :rtype json_extracted_results: dict + """ + # different versions have different parsers + json_extracted_results = {"query_successful": "", "data": []} + if self.api_version == 2: + # first elem is a context line, last two are a context line and a empty line + for item in result_text.split("\n"): + if item: + new_element = json.loads(item) + # if there is a response element it is wrapped in "obj" field + if new_element.get("obj", {}): + e = new_element["obj"] + json_extracted_results["data"].append(e) + # elements are ordered, the begin set the flag to false, + # but if the last element is succeeded and set it to true + json_extracted_results["query_successful"] = new_element.get( + "cond", "" + ) + elif self.api_version == 1: + json_extracted_results["query_successful"] = "not supported for v1" + if not self.no_results_found: + for item in result_text.split("\n"): + if item: + # in case of no results or error + if "Error" not in item: + json_extracted_results["data"].append(json.loads(item)) + else: + raise AnalyzerRunException( + f"{self.api_version} not supported version, " + f"available versions: {_supported_api_version}" + ) + + return json_extracted_results + + def _get_health_check_url(self, user: User = None) -> Optional[str]: + params = self._config.parameters.annotate_configured( + self._config, user + ).annotate_value_for_user(self._config, user) + server = params.get(name="server") + api_version = self._get_version_endpoint(params.get(name="api_version").value) + + return f"https://{server}{api_version}" + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + json_data={}, + status_code=200, + text='{"cond":"begin"}\n' + '{"obj":{"count":1,"zone_time_first":1349367341,' + '"zone_time_last":1440606099,"rrname":"mocked.data.net.",' + '"rrtype":"A","bailiwick":"net.",' + '"rdata":"0.0.0.0"}}\n' + '{"cond":"limited","msg":"Result limit reached"}\n', + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dnstwist.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dnstwist.py new file mode 100644 index 0000000..23cc0c7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/dnstwist.py @@ -0,0 +1,75 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from ipaddress import AddressValueError, IPv4Address +from unittest.mock import patch +from urllib.parse import urlparse + +# prints generated by import warnings are normal for this package +import dnstwist +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import if_mock_connections + +logger = logging.getLogger(__name__) + + +class DNStwist(classes.ObservableAnalyzer): + DNS_TWIST_PATH = settings.BASE_DIR / "dnstwist-dictionaries" + + tld_dict: str + language_dict: str + fuzzy_hash: str + fuzzy_hash_url: str + mxcheck: bool + user_agent: str + nameservers: str + + def run(self): + domain = self.observable_name + if self.observable_classification == self.ObservableTypes.URL: + domain = urlparse(self.observable_name).hostname + try: + IPv4Address(domain) + except AddressValueError: + pass + else: + raise AnalyzerRunException( + "URL with an IP address instead of a domain cannot be analyzed" + ) + + params = {"domain": domain, "registered": True, "format": "json"} + + if self.fuzzy_hash: + params["lsh"] = self.fuzzy_hash + if self.fuzzy_hash_url: + params["lsh-url"] = self.fuzzy_hash_url + if self.mxcheck: + params["mxcheck"] = True + if self.tld_dict: + params["tld"] = self.DNS_TWIST_PATH / self.tld_dict + if self.language_dict: + params["dictionary"] = self.DNS_TWIST_PATH / self.language_dict + if self.nameservers: + params["nameservers"] = self.nameservers + if self.user_agent: + params["useragent"] = self.user_agent + + report = dnstwist.run(**params) + + return report + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "dnstwist.run", + return_value={}, + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/docguard_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/docguard_get.py new file mode 100644 index 0000000..b938bc5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/docguard_get.py @@ -0,0 +1,70 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class DocGuard_Hash(classes.ObservableAnalyzer): + url: str = "https://api.docguard.net:8443/api/FileAnalyzing/GetByHash/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + @property + def hash_type(self): + hash_lengths = {32: "md5", 64: "sha256"} + hash_type = hash_lengths.get(len(self.observable_name)) + if not hash_type: + raise AnalyzerRunException( + f"Given Hash: '{hash}' is not supported." + "Supported hash types are: 'md5', 'sha256'." + ) + return hash_type + + def run(self): + headers = {} + # optional API key + if hasattr(self, "_api_key_name"): + headers["x-api-key"] = self._api_key_name + else: + warning = "No API key retrieved" + logger.info( + f"{warning}. Continuing without API key..." f" <- {self.__repr__()}" + ) + self.report.errors.append(warning) + + uri = f"{self.observable_name}" + if self.observable_classification == self.ObservableTypes.HASH: + try: + response = requests.get(self.url + uri, headers=headers) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + else: + raise AnalyzerRunException("Please use hash") + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/emailrep.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/emailrep.py new file mode 100644 index 0000000..4d7d46c --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/emailrep.py @@ -0,0 +1,56 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class EmailRep(classes.ObservableAnalyzer): + url: str = "https://emailrep.io/{}" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + """ + API key is not mandatory, emailrep supports requests with no key: + a valid key let you to do more requests per day. + therefore we're not checking if a key has been configured. + """ + + headers = { + "User-Agent": "IntelOwl", + "Key": self._api_key_name, + "Accept": "application/json", + } + + if self.observable_classification not in [self.ObservableTypes.GENERIC]: + raise AnalyzerRunException( + f"not supported observable type {self.observable_classification}." + f" Supported: generic" + ) + + url = self.url.format(self.observable_name) + + response = requests.get(url, headers=headers) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/feodo_tracker.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/feodo_tracker.py new file mode 100644 index 0000000..c7369f2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/feodo_tracker.py @@ -0,0 +1,139 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +import logging +import os +from typing import Tuple + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Feodo_Tracker(classes.ObservableAnalyzer): + """ + Feodo Tracker offers various blocklists, + helping network owners to protect their + users from Dridex and Emotet/Heodo. + """ + + use_recommended_url: bool + update_on_run: bool = True + + @classmethod + @property + def recommend_locations(cls) -> Tuple[str, str]: + db_name = "feodotracker_abuse_ipblocklist.json" + url = "https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.json" + return f"{settings.MEDIA_ROOT}/{db_name}", url + + @classmethod + @property + def default_locations(cls) -> Tuple[str, str]: + db_name = "feodotracker_abuse_ipblocklist_recommended.json" + url = "https://feodotracker.abuse.ch/downloads/ipblocklist.json" + return f"{settings.MEDIA_ROOT}/{db_name}", url + + def run(self): + result = {"found": False} + db_location, _ = ( + self.recommend_locations + if self.use_recommended_url + else self.default_locations + ) + if self.update_on_run or not os.path.exists(db_location) and not self.update(): + raise AnalyzerRunException("Unable to update database") + try: + with open(db_location, "r", encoding="utf-8") as f: + db = json.load(f) + # db is a list of dictionaries + for ip in db: + if ip["ip_address"] == self.observable_name: + result["found"] = True + break + except json.JSONDecodeError as e: + raise AnalyzerRunException(f"Decode JSON in run: {e}") + except FileNotFoundError as e: + raise AnalyzerRunException(f"File not found in run: {e}") + except KeyError as e: + raise AnalyzerRunException(f"Key error in run: {e}") + return result + + @classmethod + def update(cls) -> bool: + """ + Simply update the database + """ + for db_location, db_url in [cls.default_locations, cls.recommend_locations]: + logger.info(f"starting download of db from {db_url}") + + try: + r = requests.get(db_url) + r.raise_for_status() + except requests.RequestException: + return False + with open(db_location, "w", encoding="utf-8") as f: + try: + json.dump(r.json(), f) + except json.JSONDecodeError: + return False + logger.info(f"ended download of db from Feodo Tracker at {db_location}") + return True + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + [ + { + "ip_address": "196.218.123.202", + "port": 13783, + "status": "offline", + "hostname": "host-196.218.123.202-static.tedata.net", + "as_number": 8452, + "as_name": "TE-AS TE-AS", + "country": "EG", + "first_seen": "2023-10-23 17:04:20", + "last_online": "2024-02-06", + "malware": "Pikabot", + }, + { + "ip_address": "51.161.81.190", + "port": 13721, + "status": "offline", + "hostname": None, + "as_number": 16276, + "as_name": "OVH", + "country": "CA", + "first_seen": "2023-12-18 18:29:21", + "last_online": "2024-01-23", + "malware": "Pikabot", + }, + { + "ip_address": "185.117.90.142", + "port": 2222, + "status": "offline", + "hostname": None, + "as_number": 59711, + "as_name": "HZ-EU-AS", + "country": "NL", + "first_seen": "2024-01-17 18:58:25", + "last_online": "2024-01-22", + "malware": "QakBot", + }, + ], + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/filescan_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/filescan_search.py new file mode 100644 index 0000000..c74749b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/filescan_search.py @@ -0,0 +1,55 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import base64 + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class FileScanSearch(ObservableAnalyzer): + """FileScan_Search analyzer""" + + url: str = "https://www.filescan.io/api/reports/search" + _api_key: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + """Runs the FileScan_Search analyzer""" + observable_name_base64 = base64.b64encode( + self.observable_name.encode() + ).decode() + endpoint = "?query={input}" + url = f"{self.url}/{endpoint.format(input=observable_name_base64)}" + try: + response = requests.get(url, headers={"X-Api-Key": self._api_key}) + response.raise_for_status() + except requests.RequestException as error: + raise AnalyzerRunException(error) + return {**response.json(), "query": observable_name_base64} + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + { + "items": [], + "count": 0, + "count_search_params": 1, + "method": "and", + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/firehol_iplist.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/firehol_iplist.py new file mode 100644 index 0000000..e0879a6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/firehol_iplist.py @@ -0,0 +1,124 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import ipaddress +import logging +import os +import traceback +from datetime import datetime + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +db_path = f"{settings.MEDIA_ROOT}" + + +class FireHol_IPList(classes.ObservableAnalyzer): + list_names: list + + def run(self): + ip = self.observable_name + result = {} + + if not self.list_names: + raise AnalyzerConfigurationException( + "list_names is empty in custom analyzer config, add an iplist" + ) + + for list_name in self.list_names: + result[list_name] = False + + self.check_iplist_status(list_name) + + with open(f"{db_path}/{list_name}", "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + + for ip_or_subnet in db_list: + if ip_or_subnet and ipaddress.ip_address(ip) in ipaddress.ip_network( + ip_or_subnet + ): + result[list_name] = True + break + + return result + + @staticmethod + def download_iplist(list_name): + if ".ipset" not in list_name and ".netset" not in list_name: + raise AnalyzerConfigurationException( + f"extension missing from {list_name} (add .ipset or .netset to name)" + ) + + try: + iplist_location = f"{db_path}/{list_name}" + data_cleaned = "" + + logger.info(f"starting download of {list_name} from firehol iplist") + url = f"https://iplists.firehol.org/files/{list_name}" + r = requests.get(url) + r.raise_for_status() + + data_extracted = r.content.decode() + + for line in data_extracted.splitlines(): + if not line.startswith("#"): + data_cleaned += f"{line}\n" + + with open(iplist_location, "w", encoding="utf-8") as f: + f.write(data_cleaned) + + if not os.path.exists(iplist_location): + raise AnalyzerRunException(f"failed extraction of {list_name} iplist") + + logger.info(f"ended download of {list_name} from firehol iplist") + + except Exception as e: + traceback.print_exc() + logger.exception(e) + + def check_iplist_status(self, list_name): + iplist_location = f"{db_path}/{list_name}" + + if not os.path.exists(iplist_location): + self.download_iplist(list_name) + + now = datetime.now() + timestamp = os.path.getctime(iplist_location) + dt_object = datetime.fromtimestamp(timestamp) + time_diff = now - dt_object + + if time_diff.days < 1: + logger.info("iplist is up to date") + else: + os.remove(iplist_location) + self.download_iplist(list_name) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + json_data={}, + status_code=200, + text="""0.0.0.0/8\n + 1.10.16.0/20\n + 1.19.0.0/16\n + 3.90.198.217\n""", + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greedybear.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greedybear.py new file mode 100644 index 0000000..304872f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greedybear.py @@ -0,0 +1,40 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class GreedyBear(ObservableAnalyzer): + _api_key_name: str + url: str + + def run(self): + headers = { + "Authorization": "Token " + self._api_key_name, + "Accept": "application/json", + } + params_ = { + "query": self.observable_name, + } + uri = "/api/enrichment" + response = requests.get(self.url + uri, params=params_, headers=headers) + response.raise_for_status() + + result = response.json() + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greynoise_labs.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greynoise_labs.py new file mode 100644 index 0000000..16b90c1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greynoise_labs.py @@ -0,0 +1,216 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import os + +import requests +from django.conf import settings + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.models import PluginConfig +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +url = "https://api.labs.greynoise.io/1/query" +db_name = "topc2s_ips.txt" +db_location = f"{settings.MEDIA_ROOT}/{db_name}" + +queries = { + "noiserank": { + "query_string": "query NoiseRank($ip: String) { noiseRank(ip: $ip) \ + { queryInfo { resultsAvailable resultsLimit } ips { ip noise_score \ + sensor_pervasiveness country_pervasiveness payload_diversity \ + port_diversity request_rate } } }", + "ip_required": True, + }, + "topknocks": { + "query_string": "query TopKnocks($ip: String) { topKnocks(ip: $ip) \ + { queryInfo { resultsAvailable resultsLimit } knock { last_crawled \ + last_seen source_ip knock_port title favicon_mmh3_32 \ + favicon_mmh3_128 jarm ips emails links tor_exit headers apps } } } ", + "ip_required": True, + }, + "topc2s": { + "query_string": "query TopC2s { topC2s { queryInfo \ + { resultsAvailable resultsLimit } c2s { source_ip c2_ips \ + c2_domains payload hits pervasiveness } } } ", + "ip_required": False, + "db_location": db_location, + }, +} + + +class GreynoiseLabs(ObservableAnalyzer): + _auth_token: str + + def run(self): + result = {} + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self._auth_token}", + } + + for key, value in queries.items(): + if not value["ip_required"]: + if not os.path.isfile(value["db_location"]) and not self.update(): + error_message = f"Failed extraction from {key} db" + self.report.errors.append(error_message) + self.report.save() + logger.error(error_message) + continue + + with open(value["db_location"], "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + if self.observable_name in db_list: + result[key] = {"found": True} + else: + result[key] = {"found": False} + continue + + json_body = { + "query": value["query_string"], + "variables": {"ip": f"{self.observable_name}"}, + } + response = requests.post(headers=headers, json=json_body, url=url) + response.raise_for_status() + result[key] = response.json() + + return result + + @classmethod + def _get_auth_token(cls): + for plugin in PluginConfig.objects.filter( + parameter__python_module=cls.python_module, + parameter__is_secret=True, + parameter__name="auth_token", + ): + if plugin.value: + return plugin.value + return None + + @classmethod + def _update_db(cls, auth_token: str): + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {auth_token}", + } + + try: + logger.info("Fetching data from greynoise API (Greynoise_Labs).....") + response = requests.post( + headers=headers, + json={"query": queries["topc2s"]["query_string"]}, + url=url, + ) + response.raise_for_status() + topc2s_data = response.json() + + with open(db_location, "w", encoding="utf-8") as f: + for value in topc2s_data["data"]["topC2s"]["c2s"]: + ip = value["source_ip"] + if ip: + f.write(f"{ip}\n") + + if not os.path.exists(db_location): + return False + + logger.info("Data fetched from greynoise API (Greynoise_Labs).....") + return True + except Exception as e: + logger.exception(e) + + @classmethod + def update(cls): + auth_token = cls._get_auth_token() + if auth_token: + return cls._update_db(auth_token=auth_token) + return False + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + side_effect=[ + MockUpResponse( + { + "data": { + "noiseRank": { + "queryInfo": { + "resultsAvailable": 1, + "resultsLimit": 1, + }, + "ips": [ + { + "ip": "20.235.249.22", + "noise_score": 12, + "sensor_pervasiveness": "very low", + "country_pervasiveness": "low", + "payload_diversity": "very low", + "port_diversity": "very low", + "request_rate": "low", + } + ], + } + } + }, + 200, + ), + MockUpResponse( + { + "data": { + "topKnocks": { + "queryInfo": { + "resultsAvailable": 1, + "resultsLimit": 1, + }, + } + } + }, + 200, + ), + MockUpResponse( + { + "data": { + "topC2s": { + "queryInfo": { + "resultsAvailable": 1914, + "resultsLimit": 191, + }, + "c2s": [ + { + "source_ip": "91.92.247.12", + "c2_ips": ["103.245.236.120"], + "c2_domains": [], + "hits": 11608, + }, + { + "source_ip": "14.225.208.190", + "c2_ips": ["14.225.213.142"], + "c2_domains": [], + "hits": 2091, + "pervasiveness": 26, + }, + { + "source_ip": "157.10.53.101", + "c2_ips": ["14.225.208.190"], + "c2_domains": [], + "hits": 1193, + "pervasiveness": 23, + }, + ], + }, + }, + }, + 200, + ), + ], + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greynoiseintel.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greynoiseintel.py new file mode 100644 index 0000000..75b1d95 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/greynoiseintel.py @@ -0,0 +1,80 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from greynoise import GreyNoise +from greynoise.exceptions import NotFound, RateLimitError, RequestFailure + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class GreyNoiseAnalyzer(classes.ObservableAnalyzer): + greynoise_api_version: str + max_records_to_retrieve: int + + _api_key_name: str = None + + @classmethod + def update(cls) -> bool: + pass + + @property + def integration_name(self): + if self.greynoise_api_version == "v2": + return "greynoise-intelowl-v1.0" + elif self.greynoise_api_version == "v3": + return "greynoise-community-intelowl-v1.0" + raise RuntimeError(f"Version {self.greynoise_api_version} not configured") + + def run(self): + response = {} + if self.greynoise_api_version == "v2": + session = GreyNoise( + api_key=self._api_key_name, + integration_name=self.integration_name, + ) + elif self.greynoise_api_version == "v3": + session = GreyNoise( + api_key=self._api_key_name, + integration_name=self.integration_name, + offering="Community", + ) + else: + raise AnalyzerRunException( + "Invalid API Version. Supported are: v2 (paid), v3 (community)" + ) + try: + response = session.ip(self.observable_name) + if self.greynoise_api_version == "v2": + response |= session.riot(self.observable_name) + # greynoise library does provide empty messages in case of these errors... + # so it's better to catch them and create custom management + except RateLimitError as e: + self.disable_for_rate_limit() + self.report.errors.append(e) + self.report.save() + raise AnalyzerRunException(f"Rate limit error: {e}") + except RequestFailure as e: + self.report.errors.append(e) + self.report.save() + raise AnalyzerRunException(f"Request failure error: {e}") + except NotFound as e: + logger.info(f"not found error for {self.observable_name} :{e}") + response["not_found"] = True + + return response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch.object(GreyNoise, "ip", return_value={"noise": True}), + patch.object(GreyNoise, "riot", return_value={"riot": True}), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ha_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ha_get.py new file mode 100644 index 0000000..4146675 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ha_get.py @@ -0,0 +1,82 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class HybridAnalysisGet(ObservableAnalyzer): + url: str = "https://www.hybrid-analysis.com" + api_url: str = f"{url}/api/v2/" + sample_url: str = f"{url}/sample" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = { + "api-key": self._api_key_name, + "user-agent": "Falcon Sandbox", + "accept": "application/json", + } + obs_clsfn = self.observable_classification + + if obs_clsfn == self.ObservableTypes.DOMAIN: + data = {"domain": self.observable_name} + uri = "search/terms" + elif obs_clsfn == self.ObservableTypes.IP: + data = {"host": self.observable_name} + uri = "search/terms" + elif obs_clsfn == self.ObservableTypes.URL: + data = {"url": self.observable_name} + uri = "search/terms" + elif obs_clsfn == self.ObservableTypes.HASH: + data = {"hash": self.observable_name} + uri = "search/hash" + else: + raise AnalyzerRunException( + f"not supported observable type {obs_clsfn}. " + "Supported are: hash, ip, domain and url" + ) + + response = requests.post(self.api_url + uri, data=data, headers=headers) + response.raise_for_status() + + result = response.json() + # adding permalink to results + if isinstance(result, list): + for job in result: + sha256 = job.get("sha256", "") + job_id = job.get("job_id", "") + if sha256: + job["permalink"] = f"{self.sample_url}/{sha256}" + if job_id: + job["permalink"] += f"/{job_id}" + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse( + [ + { + "job_id": "1", + "sha256": "abcdefgh", + } + ], + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hashlookup.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hashlookup.py new file mode 100644 index 0000000..61fbd52 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hashlookup.py @@ -0,0 +1,37 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from pyhashlookup import Hashlookup, PyHashlookupError + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockResponseNoOp, if_mock_connections, patch + + +class HashLookupServer(classes.ObservableAnalyzer): + hashlookup_server: str + + def run(self): + if self.hashlookup_server: + hashlookup_instance = Hashlookup(root_url=self.hashlookup_server) + else: + # the library maintains the default URL + hashlookup_instance = Hashlookup() + + try: + result = hashlookup_instance.lookup(self.observable_name) + except PyHashlookupError as e: + raise AnalyzerRunException(e) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "pyhashlookup.Hashlookup", return_value=MockResponseNoOp({}, 200) + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/haveibeenpwned.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/haveibeenpwned.py new file mode 100644 index 0000000..b4dee7f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/haveibeenpwned.py @@ -0,0 +1,50 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class HaveIBeenPwned(classes.ObservableAnalyzer): + url: str = "https://haveibeenpwned.com/api/v3/breachedaccount/" + + truncate_response: bool + include_unverified: bool + domain: str + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + params = { + "truncateResponse": self.truncate_response, + "includeUnverified": self.include_unverified, + } + if self.domain: + params["domain"] = self.domain + + headers = {"hibp-api-key": self._api_key_name} + + response = requests.get( + self.url + self.observable_name, params=params, headers=headers + ) + response.raise_for_status() + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/honeydb.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/honeydb.py new file mode 100644 index 0000000..e165654 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/honeydb.py @@ -0,0 +1,91 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from typing import Dict + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerConfigurationException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class HoneyDB(classes.ObservableAnalyzer): + url = "https://honeydb.io/api" + # set secrets + _api_key_name: str + _api_id_name: str + honeydb_analysis: str + + @classmethod + def update(cls) -> bool: + pass + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.headers = { + "X-HoneyDb-ApiKey": self._api_key_name, + "X-HoneyDb-ApiId": self._api_id_name, + } + self.result = {} + self.endpoints = [ + "scan_twitter", + "ip_query", + "ip_history", + "internet_scanner", + "ip_info", + ] + if ( + self.honeydb_analysis not in self.endpoints + and self.honeydb_analysis != "all" + ): + raise AnalyzerConfigurationException( + f"analysis_type is not valid: {self.honeydb_analysis}" + ) + + def run(self): + if self.honeydb_analysis == "all": + for endpoint in self.endpoints: + self._request_analysis(endpoint) + else: + self._request_analysis(self.honeydb_analysis) + + return self.result + + def _request_analysis(self, endpoint): + if endpoint == "scan_twitter": + url = f"{self.url}/twitter-threat-feed/{self.observable_name}" + elif endpoint == "ip_query": + url = f"{self.url}/netinfo/lookup/{self.observable_name}" + elif endpoint == "ip_history": + url = f"{self.url}/ip-history/{self.observable_name}" + elif endpoint == "internet_scanner": + url = f"{self.url}/internet-scanner/info/{self.observable_name}" + elif endpoint == "ip_info": + url = f"{self.url}/ipinfo/{self.observable_name}" + else: + logger.error(f"endpoint {endpoint} not supported") + return + try: + response = requests.get(url, headers=self.headers) + response.raise_for_status() + except Exception as e: + logger.exception(e) + self.result[endpoint] = {"error": e} + else: + self.result[endpoint] = response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hudsonrock.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hudsonrock.py new file mode 100644 index 0000000..cd46a3e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hudsonrock.py @@ -0,0 +1,620 @@ +import logging +import re + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerConfigurationException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class HudsonRock(classes.ObservableAnalyzer): + """ + This analyzer is a wrapper for hudson rock + """ + + compromised_since: str = None # for IP/DOMAIN + compromised_until: str = None # for IP/DOMAIN + page: int = None # for IP/LOGIN/DOMAIN + added_since: str = None # for IP/DOMAIN + added_until: str = None # for IP/DOMAIN + installed_software: bool = None # for IP/LOGIN/DOMAIN + sort_by: str = None # for LOGIN + domain_cred_type: str = None # for DOMAIN + domain_filtered: bool = None # for DOMAIN + third_party_domains: bool = None # for DOMAIN + + _api_key_name: str + + url = "https://cavalier.hudsonrock.com/api/json/v2" + + def get_param_url(self, valid_params): + param_url = "" + params = { + "compromised_since": self.compromised_since, + "compromised_until": self.compromised_until, + "page": self.page, + "added_since": self.added_since, + "added_until": self.added_until, + "installed_software": self.installed_software, + "sortby": self.sort_by, + "type": self.domain_cred_type, + "filtered": self.domain_filtered, + "third_party_domains": self.third_party_domains, + } + for param, value in params.items(): + if param in valid_params and value: + param_url += f"&{param}={value}" + + return "?" + param_url + + def run(self): + response = {} + headers = { + "api-key": self._api_key_name, + "Content-Type": "application/json", + } + if self.observable_classification == self.ObservableTypes.IP: + url = ( + self.url + + "/search-by-ip" + + self.get_param_url( + [ + "compromised_since", + "compromised_until", + "page", + "added_since", + "added_until", + "installed_software", + ] + ) + ) + response = requests.post( + url, headers=headers, json={"ip": self.observable_name} + ) + + elif self.observable_classification == self.ObservableTypes.DOMAIN: + url = ( + self.url + + "/search-by-domain" + + self.get_param_url( + [ + "compromised_since", + "compromised_until", + "page", + "added_since", + "added_until", + "installed_software", + "type", + "filtered", + "third_party_domains", + ] + ) + ) + response = requests.post( + url, headers=headers, json={"domains": [self.observable_name]} + ) + + elif self.observable_classification == self.ObservableTypes.GENERIC: + # checking for email + regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b" + if re.fullmatch(regex, self.observable_name): + url = ( + self.url + + "/search-by-login" + + self.get_param_url(["sortby", "page", "installed_software"]) + ) + response = requests.post( + url, headers=headers, json={"login": self.observable_name} + ) + else: + raise AnalyzerConfigurationException( + f"Invalid observable type {self.observable_classification}" + + f"{self.observable_name} for HudsonRock" + ) + response.raise_for_status() + return response.json() + + def update(self) -> bool: + pass + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse( + { + "date_uploaded": "2019-11-07T04:38:32.024Z", + "stealer_family": "Vidar", + "antiviruses": "Not Found", + "employee_session_cookies": "null", + "date_compromised": "2019-11-04T13:33:47.000Z", + "credentials": [ + { + "type": "client", + "url": "••••••••••••••", + "domain": "disney.com", + "username": "•••••••", + "password": "••••••••", + } + ], + "stealer": "••••••••••••", + "employeeAt": ["••••••••••"], + "clientAt": [ + "•••••••••••••••", + "•••••••••••", + "••••••••••••", + "•••••••••", + "•••••••••••••", + "••••••••••••••••••", + "•••••••••", + "•••••••••", + "••••••••••", + "•••••••••••", + "••••••••••••", + "••••••••••••••", + "•••••••••", + "•••••••••••••", + "••••••••", + "••••••••••••••••", + "••••••••••", + "••••••", + "•••••••••••", + "••••••••••••", + "••••••••••••", + "••••••••••", + "•••••••••••", + "••••••••••", + "•••••••••••••", + "••••••••••••••••", + "••••••••••••", + "•••••••••••", + "••••••••••••••••", + "•••••••••••••", + "•••••••••", + "••••••••••••", + "••••••••", + "••••••••••", + "•••••••", + "•••••••••••••", + "••••••••••••••••", + "•••••••••••••••••••", + "••••••", + "••••••••••", + "••••••••••••••••••", + "••••••••••••••••", + "•••••••••", + "•••••••••••", + "••••••", + "•••••••••", + "•••••••••", + "••••••••••", + "•••••••••••••••", + ], + "ip": "••••••••••••••", + "malware_path": "••••••••••••••", + }, + { + "date_uploaded": "2019-11-06T21:51:35.676Z", + "stealer_family": "Vidar", + "antiviruses": "Not Found", + "employee_session_cookies": "null", + "date_compromised": "2019-11-03T20:39:11.000Z", + "credentials": [ + { + "type": "client", + "url": "•••••••••••••••", + "domain": "disney.com", + "username": "•••", + "password": "•••••••••", + } + ], + "stealer": "••••••••••••••••••••••••••••••••", + "employeeAt": ["•••••••••••", "••••••••••", "••••••"], + "clientAt": [], + "ip": "•••••••••••••", + "malware_path": "•••••••••••••••••••", + }, + { + "date_uploaded": "2021-07-11T12:17:51.429Z", + "stealer_family": "RedLine", + "computer_name": "This PC", + "operating_system": "Windows 10 Home x64", + "antiviruses": "Norton 360", + "employee_session_cookies": "null", + "date_compromised": "2021-07-06T23:27:32.000Z", + "credentials": [ + { + "type": "client", + "url": "••••••••", + "domain": "disney.com", + "username": "••••••", + "password": "••••••••••", + } + ], + "stealer": "•••••••••••••••••••", + "employeeAt": ["•••••••••", "••••••••", "••••••••••"], + "clientAt": [ + "•", + "•", + "••••", + "••••••••••••••", + "•••••••••", + "••••••••••", + "•••••••••••", + "•••••••••", + "••••••••••••", + "•••••••••••••••••••", + "••••••••••••••••", + "•••••••••••••••••", + "••••••••••••", + "••••••••••", + "••••••••••••••••••", + "•••••••••••", + "••••••••••••", + "••••••••••••", + "••••••••", + "•••••••••••••••••", + "••••••••••••••••", + "••••••••••••••", + "•••••••••••", + "••••••••••••", + "•••••••••••••••••", + "•••••••••••••••••••••", + "•••••••••••••••••••", + "••••••••••••••••••••", + "•••••••••••••••••••••••", + "•••••••••••••", + "•••••••••••••••••••••", + "•••••••••••••••••••••••", + "••••••••", + "••••••••••••••••••••", + "•••••••••••••••••", + "•••••••••••••••••", + "•••••••••••", + "••••••••••", + "••••••••••••••", + "••••••••••••", + "•••••••••••", + "••••••••••", + "••••••••••", + "••••••••••••", + "••••••••••••••••", + "•••••••••••••", + "•••••••••", + "•••••••••••", + "•••••••", + "•••••••", + "••••••••••", + "••••••", + "••••••••", + "••••••••••", + "••••••••••••••••", + "••••••••••••", + "•••••••", + "•••••••••••••••", + "••••••••••", + "••••••••••••••••••", + "•••••••", + "••••••••", + "•••••••", + "•••••••••••••", + "•••••••••••", + "••••••••••", + "••••••••••••••••", + "•••••••••••", + "•••••••••••", + "•••••••••••••••", + "•••••••••••", + "•••••••", + "•••••••••••••", + "•••••••••••••", + "•••••••••••••", + "•••••••••••••••••", + "••••••••", + "••••••••••••", + "••••••••••••", + "••••••••", + "••••••••••••••", + "•••••••••••", + "•••••••••••••", + "••••••••••••••••", + "•••••••••••", + "•••••••", + "•••••••••", + "••••••••••••", + "•••••••••••••••", + "•••••••••••••••", + "•••••••••", + "•••••••••••", + "••••••••", + "•••••••••••", + "••••••••••••••", + "•••••••••••", + "••••••••••••••", + "••••••••••••••••", + "••••••••••••••••", + "•••••••••••••••", + "••••••••", + "•••••••••••", + "•••••••••••", + "••••••••••••••••", + "•••", + "••••••••", + "•••••••••", + ], + "ip": "••••••••••••••", + "malware_path": "•••••••••••••", + }, + { + "date_uploaded": "2021-05-06T05:58:56.299Z", + "stealer_family": "RedLine", + "computer_name": "jskho", + "operating_system": "Windows 10 Home x64", + "antiviruses": "Norton Security", + "employee_session_cookies": "null", + "date_compromised": "2021-05-06T01:31:09.000Z", + "credentials": [ + { + "type": "client", + "url": "•••••••••••••••••", + "domain": "disney.com", + "username": "•••••••@gmail.com", + "password": "••••••••", + } + ], + "stealer": "•••••••••••••••", + "employeeAt": [], + "clientAt": [ + "••••••••••••••", + "•••••••••••••••••••", + "•••••••••••", + "•", + "•", + "••••", + "••••••••••••••••", + "••••••••••••••••••••", + "•••••••••••••••••••••", + "••••••••••••", + "••••••••••", + "•••••••••••••••", + "•••••••••", + "•••••", + "••••••••••••", + "•••••••••••••", + "••••••••••••••••••••••", + "•••••••••••••••••", + "•••••••••••••", + "•••••••••••", + "••••••••••••", + "••••••••", + "•••••••••", + "••••••••••••••", + "••••••", + "••••••••••••", + "••••••••••••••••", + "••••••••••••••", + "•••••••••••••••", + "•••••••••••••", + "•••••••••••", + "•••••••••••••••••••", + "•••••••••••••••••••", + "••••••••", + "••••••••••••••••••", + "•••••••••", + "•••••••••", + "•••••••••••••••••••••", + "•••••••••••••••••••••", + "•••••••••••••••••••••", + "••••••••••••••••", + "•••••••••••••••••••", + "•••••••••••••••••••••", + "••••••••••", + "•••••••••••••••••••••", + "••••••••••••••••••••••••••", + "•••••••••••••", + "•••••••", + "••••••••••••••", + "••••••••••", + "••••••••••", + "•••••••••••", + "•••••••••", + "••••••••", + "••••••", + "••••••", + "•••••••••••", + "•••••••••••••••••••", + "•••••••••••••••", + "••••••••••", + "•••••••••••••••••", + "••••••••••••", + "••••••••••••••••••", + "•••••••••", + "••••••••••••", + "••••••••••••••••••", + "•••••••••", + "••••••", + "••••••••••", + "••••••••••••••••••", + "••••••••••", + "•••••••••••", + "••••••••••••", + "••••••••••••", + "•••••••••••••", + "••••••••••••••••••••", + "•••••", + "••••••••", + "•••••••••••", + "••••••••••••••", + "•••••••••••••••", + "•••••••••••••", + "•••••••••••••", + "••••••••••", + "••••••••••", + "••••••••••••••••", + "••••••••", + "•••••••••", + "••••••••••", + "•••••••••••••", + "••••••••••", + "•••••••••••", + "••••••••••••••", + "••••••••", + "•••••••••••••••••", + "••••••••••••••••••", + "•••••••••", + "•••••••••••••", + "••••••••••••••••••", + "•••••••••••••••", + "•••••••••", + "••••••••••••••", + "•••••••••••••••••", + "••••••••••••••••", + "••••••••••••••••", + "•••••••••••", + "•••••••••••••••••••••••••", + "•••••••••••", + "••••••••••••", + "••••••••••••••", + "•••••••••", + "••••••••", + "••••••••••••••••••••", + "••••••••••••••••", + "•••••••••••••", + "•••••••••••••••••••••", + "••••••••••••••••", + "•••••••••••••••", + "•••••••••••••", + "••••••••••", + "•••••••••••••", + "•••••••••••••", + "••••••••••••", + "••••••••••••••••", + "••••••••••••", + "•••••••••", + "••••••••••••", + "••••••••••••", + "•••••••••••••••••", + "•••••••••••••", + "•••••••••••••", + "••••••••", + "•••••••••••••••••••••", + "••••••••••••", + "••••••••••••", + "••••••••••••", + "••••••••••••••••••••••", + "•••••••••", + "•••••••••••••••••••••", + "••••••••••••••", + "•••••••••••••••••••", + "•••••••••••••", + "•••••••••••", + "••••••••••", + "•••••••••••", + "•••••••••••", + "•••••••••••••••••••", + "••••••••••••••••••", + "••••••••••••••••", + "•••••••••", + "••••••••••••••••••", + "••••••••••••••••", + "•••••••••••••", + "••••••••••••••••", + "••••••••••••••••••••", + "••••••••••••", + "•••••••••••", + "••••••••••", + "•••••••••••••••••", + "•••••••••••••••••", + "•••••••••••••", + "•••••••••••••••", + "••••••••", + "••••••••••", + "•••••••••••••••••••••", + "•••••••••", + "•••••••••••", + "•••••••••••••••", + "•••••••••••", + "•••••••", + "••••••••", + "••••••••", + "•••••••••", + "•••••••••••••••", + "••••••••••••••••", + "••••••••••••••••••", + "•••••••••", + "••••••••••••••••", + "•••••••", + "•••", + ], + "ip": "•••••••••••••", + "malware_path": "••••••", + }, + { + "date_uploaded": "2021-04-02T11:46:34.357Z", + "stealer_family": "RedLine", + "computer_name": "samih", + "operating_system": "Windows 10 Home x64", + "antiviruses": "Windows Defender", + "employee_session_cookies": "null", + "date_compromised": "2021-03-31T18:23:31.000Z", + "credentials": [ + { + "type": "client", + "url": "", + "domain": "disney.com", + "username": "••••••••", + "password": "••••••••••", + }, + { + "type": "client", + "url": "•••••••••••••••••", + "domain": "disney.com", + "username": "••••••••", + "password": "••••••••••", + }, + ], + "stealer": "••••••••••••••••••••", + "employeeAt": [], + "clientAt": [ + "•", + "•", + "••••", + "•••••••••••••••••••", + "••••••••••••••••", + "•••••••••••••••••", + "••••••••••", + "•••••••••••", + "••••••", + "••••••••", + "••••••••••••", + "••••••••••", + "•••••••••", + "•••••••••••••••••••", + "••••••••••••••••••", + "••••••••••••", + "••••••••••", + "••••••••••", + "••••••••••", + "•••••••••••••••••", + "••••••••••", + "•••••••••••••", + "••••••••", + "••••••••••••••••••••••••••••", + "••••••••••••••••", + "•••••••••", + "•••", + ], + "ip": "••••••••••••••", + "malware_path": "•••••", + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hunter_how.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hunter_how.py new file mode 100644 index 0000000..26dc48d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hunter_how.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import base64 +from typing import Dict + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Hunter_How(classes.ObservableAnalyzer): + url: str = "https://api.hunter.how/search" + _api_key_name: str + page: int + page_size: int + start_time: str + end_time: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self.observable_classification == self.ObservableTypes.IP: + self.query = f'ip="{self.observable_name}"' + elif self.observable_classification == self.ObservableTypes.DOMAIN: + self.query = f'domain="{self.observable_name}"' + + self.encoded_query = base64.urlsafe_b64encode( + self.query.encode("utf-8") + ).decode("ascii") + self.parameters = { + "api-key": self._api_key_name, + "query": self.encoded_query, + "page": self.page, + "page_size": self.page_size, + "start_time": self.start_time, + "end_time": self.end_time, + } + + def run(self): + try: + response_ip = requests.get(self.url, params=self.parameters) + response_ip.raise_for_status() + + except requests.RequestException as e: + raise AnalyzerRunException(e) + + return response_ip.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({"list": []}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hunter_io.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hunter_io.py new file mode 100644 index 0000000..e86d265 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/hunter_io.py @@ -0,0 +1,36 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Hunter_Io(classes.ObservableAnalyzer): + url: str = "https://api.hunter.io/v2/domain-search?" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + url = f"{self.url}domain={self.observable_name}&api_key={self._api_key_name}" + response = requests.get(url) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/inquest.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/inquest.py new file mode 100644 index 0000000..ad2e95e --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/inquest.py @@ -0,0 +1,136 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import re +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class InQuest(ObservableAnalyzer): + url: str = "https://labs.inquest.net" + + _api_key_name: str + inquest_analysis: str + + @classmethod + def update(cls) -> bool: + pass + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.generic_identifier_mode = "user-defined" # Or auto + + @property + def hash_type(self): + hash_lengths = {32: "md5", 40: "sha1", 64: "sha256", 128: "sha512"} + hash_type = hash_lengths.get(len(self.observable_name)) + if not hash_type: + raise AnalyzerRunException( + f"Given Hash: '{hash}' is not supported." + "Supported hash types are: 'md5', 'sha1', 'sha256', 'sha512'." + ) + return hash_type + + def type_of_generic(self): + if re.match(r"^[\w\.\+\-]+\@[\w]+\.[a-z]{2,3}$", self.observable_name): + type_ = "email" + else: + # TODO: This should be validated more thoroughly + type_ = "filename" + return type_ + + def run(self): + headers = {"Content-Type": "application/json"} + # optional API key + if hasattr(self, "_api_key_name"): + headers["Authorization"] = self._api_key_name + else: + warning = "No API key retrieved" + logger.info( + f"{warning}. Continuing without API key..." f" <- {self.__repr__()}" + ) + self.report.errors.append(warning) + + if self.inquest_analysis == "dfi_search": + link = "dfi" + if self.observable_classification == self.ObservableTypes.HASH: + uri = ( + f"/api/dfi/search/hash/{self.hash_type}?hash={self.observable_name}" + ) + + elif self.observable_classification in [ + self.ObservableTypes.IP, + self.ObservableTypes.URL, + self.ObservableTypes.DOMAIN, + ]: + uri = ( + f"/api/dfi/search/ioc/{self.observable_classification}" + f"?keyword={self.observable_name}" + ) + + elif self.observable_classification == self.ObservableTypes.GENERIC: + try: + type_, value = self.observable_name.split(":") + except ValueError: + self.generic_identifier_mode = "auto" + type_ = self.type_of_generic() + value = self.observable_name + + if type_ not in ["email", "filename", "registry", "xmpid"]: + raise AnalyzerRunException(f"Unknown Type: {type_}") + + uri = f"/api/dfi/search/ioc/{type_}?keyword={value}" + else: + raise AnalyzerRunException() + + elif self.inquest_analysis == "iocdb_search": + uri = f"/api/iocdb/search?keyword={self.observable_name}" + link = "iocdb" + + elif self.inquest_analysis == "repdb_search": + uri = f"/api/repdb/search?keyword={self.observable_name}" + link = "repdb" + + else: + raise AnalyzerConfigurationException( + f"analysis type: '{self.inquest_analysis}' not supported." + "Supported are: 'dfi_search', 'iocdb_search', 'repdb_search'." + ) + + response = requests.get(self.url + uri, headers=headers, timeout=30) + response.raise_for_status() + result = response.json() + if ( + self.inquest_analysis == "dfi_search" + and self.observable_classification == self.ObservableTypes.HASH + ): + result["hash_type"] = self.hash_type + + if self.generic_identifier_mode == "auto": + result["type_of_generic"] = self.type_of_generic() + + result["link"] = f"https://labs.inquest.net/{link}" + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/intelx.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/intelx.py new file mode 100644 index 0000000..d20be56 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/intelx.py @@ -0,0 +1,146 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time +from typing import Dict + +import requests +from django.utils.functional import cached_property + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class IntelX(ObservableAnalyzer): + """ + Analyzer Name: `IntelX`\n + Refer to: https://github.com/IntelligenceX/SDK + Requires API Key + """ + + url: str = "https://2.intelx.io" + + _api_key_name: str + + query_type: str + rows_limit: int + max_tries: int + poll_distance: int + timeout: int + datefrom: str + dateto: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self.query_type not in ["phonebook", "intelligent"]: + raise AnalyzerConfigurationException(f"{self.query_type} not supported") + self.search_url = self.url + f"/{self.query_type}/search" + + @cached_property + def _session(self): + session = requests.Session() + session.headers.update({"x-key": self._api_key_name, "User-Agent": "IntelOwl"}) + return session + + def _poll_for_results(self, search_id): + json_data = {} + for chance in range(self.max_tries): + logger.info( + f"Result Polling. Try #{chance + 1}. Starting the query..." + f"<-- {self.__repr__()}" + ) + try: + r = self._session.get( + f"{self.search_url}/result?id={search_id}" + f"&limit={self.rows_limit}&offset=-1" + ) + r.raise_for_status() + except requests.RequestException as e: + logger.warning(f"request failed: {e}") + else: + if r.status_code == 200: + json_data = r.json() + break + time.sleep(self.poll_distance) + + if not json_data: + raise AnalyzerRunException( + "reached max tries for IntelX analysis," + f" observable {self.observable_name}" + ) + + if self.query_type == "phonebook": + selectors = json_data["selectors"] + parsed_selectors = self.__pb_search_results(selectors) + result = {"id": search_id, **parsed_selectors} + else: + result = json_data + return result + + def run(self): + params = { + "term": self.observable_name, + "buckets": [], + "lookuplevel": 0, + "maxresults": self.rows_limit, + "timeout": self.timeout, + "sort": 4, # newest items first + "media": 0, + "terminate": [], + } + if self.query_type == "phonebook": + params["target"] = 0 + elif self.query_type == "intelligent": + params["datefrom"] = self.datefrom + params["dateto"] = self.dateto + # POST the search term --> Fetch the 'id' --> GET the results using the 'id' + logger.info( + f"starting {self.query_type} request for observable {self.observable_name}" + ) + r = self._session.post(self.search_url, json=params) + r.raise_for_status() + search_id = r.json().get("id", None) + if not search_id: + raise AnalyzerRunException( + f"Failed to request search. Status code: {r.status_code}." + ) + result = self._poll_for_results(search_id) + + return result + + @staticmethod + def __pb_search_results(selectors): + """ + https://github.com/zeropwn/intelx.py/blob/master/cli/intelx.py#L89 + """ + result = {} + for block in selectors: + selectortypeh = block["selectortypeh"] + if selectortypeh not in result: + result[selectortypeh] = [] + result[selectortypeh].append(block["selectorvalue"]) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.post", + return_value=MockUpResponse({"id": 1}, 200), + ), + patch( + "requests.Session.get", + return_value=MockUpResponse({"selectors": []}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/intezer_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/intezer_get.py new file mode 100644 index 0000000..344b327 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/intezer_get.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from datetime import timedelta +from typing import Dict + +import intezer_sdk.consts +from intezer_sdk import api as intezer_api +from intezer_sdk import errors as intezer_errors +from intezer_sdk.analysis import FileAnalysis + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import if_mock_connections, patch + + +class IntezerGet(ObservableAnalyzer): + soft_time_limit: int + _api_key_name: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.timeout = self.soft_time_limit - 5 + # interval + self.poll_interval = 3 + # read secret and set API key + intezer_api.set_global_api(api_key=self._api_key_name) + intezer_sdk.consts.USER_AGENT = "IntelOwl" + + def run(self): + result = {} + try: + # run analysis + analysis = FileAnalysis(file_hash=self.observable_name) + analysis.send(wait=False) + analysis.wait_for_completion( + interval=self.poll_interval, + sleep_before_first_check=True, + timeout=timedelta(seconds=self.timeout), + ) + except (intezer_errors.HashDoesNotExistError, intezer_errors.InsufficientQuota): + result.update(hash_found=False) + except intezer_errors.IntezerError as e: + raise AnalyzerRunException(e) + except TimeoutError as e: + raise e + else: + result.update(analysis.result(), hash_found=True) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch.object(FileAnalysis, "send", return_value=None), + patch.object(FileAnalysis, "wait_for_completion", return_value=None), + patch.object(FileAnalysis, "result", return_value={"test": "test"}), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ip2location.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ip2location.py new file mode 100644 index 0000000..ac4966b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ip2location.py @@ -0,0 +1,68 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Ip2location(classes.ObservableAnalyzer): + url: str = "https://api.ip2location.io/" + _api_key_name: str + api_version: str + + @classmethod + def update(cls) -> bool: + pass + + def get_response(self, payload): + return requests.get(self.url, params=payload) + + def run(self): + try: + payload = {"ip": self.observable_name} + + # There are two free versions of the service: + # 1. keyless : Requires No API key and has a daily limit of 500 queries + # 2. keyed: Requires API key. + + if self.api_version == "keyed": + payload["key"] = self._api_key_name + + location_info = self.get_response(payload) + location_info.raise_for_status() + + except requests.RequestException as e: + raise AnalyzerRunException(e) + + response = location_info.json() + return response + + @classmethod + def _monkeypatch(cls): + sample_response = { + "ip": "8.8.8.8", + "country_code": "US", + "country_name": "United States of America", + "region_name": "California", + "city_name": "Mountain View", + "latitude": 37.405992, + "longitude": -122.078515, + "zip_code": "94043", + "time_zone": "-07:00", + "asn": "15169", + "as": "Google LLC", + "is_proxy": False, + } + + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse(sample_response, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ip2whois.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ip2whois.py new file mode 100644 index 0000000..ed770fb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ip2whois.py @@ -0,0 +1,114 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Ip2whois(classes.ObservableAnalyzer): + url: str = "https://api.ip2whois.com/v2" + _api_key_name: str + + def update(self): + pass + + def get_response(self, payload): + return requests.get(self.url, params=payload) + + def run(self): + try: + params = { + "key": self._api_key_name, + "domain": self.observable_name, + } + + location_info = self.get_response(params) + location_info.raise_for_status() + + except requests.RequestException as e: + raise AnalyzerRunException(e) + + response = location_info.json() + return response + + @classmethod + def _monkeypatch(cls): + sample_response = { + "domain": "msn.com", + "domain_id": "4569290_DOMAIN_COM-VRSN", + "status": "client delete prohibited", + "create_date": "1994-11-10T05:00:00Z", + "update_date": "2023-05-03T11:39:17Z", + "expire_date": "2024-06-04T16:44:29Z", + "domain_age": 10766, + "whois_server": "", + "registrar": {"iana_id": "292", "name": "MarkMonitor Inc.", "url": ""}, + "registrant": { + "name": "", + "organization": "", + "street_address": "", + "city": "", + "region": "", + "zip_code": "", + "country": "", + "phone": "", + "fax": "", + "email": "", + }, + "admin": { + "name": "", + "organization": "", + "street_address": "", + "city": "", + "region": "", + "zip_code": "", + "country": "", + "phone": "", + "fax": "", + "email": "", + }, + "tech": { + "name": "", + "organization": "", + "street_address": "", + "city": "", + "region": "", + "zip_code": "", + "country": "", + "phone": "", + "fax": "", + "email": "", + }, + "billing": { + "name": "", + "organization": "", + "street_address": "", + "city": "", + "region": "", + "zip_code": "", + "country": "", + "phone": "", + "fax": "", + "email": "", + }, + "nameservers": [ + "dns1.p09.nsone.net", + "ns1-204.azure-dns.com", + "ns2-204.azure-dns.net", + "ns3-204.azure-dns.org", + "ns4-204.azure-dns.info", + ], + } + + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse(sample_response, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipapi.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipapi.py new file mode 100644 index 0000000..e4025d6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipapi.py @@ -0,0 +1,49 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class IPApi(classes.ObservableAnalyzer): + batch_url = "http://ip-api.com/batch" + dns_url = "http://edns.ip-api.com/json" + + fields: str + lang: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.IP = [ + { + "query": self.observable_name, + "fields": self.fields, + "lang": self.lang, + } + ] + + def run(self): + response_batch = requests.post(self.batch_url, json=self.IP) + response_batch.raise_for_status() + + response_dns = requests.get(self.dns_url) + response_dns.raise_for_status() + + response = {"ip_info": response_batch.json(), "dns_info": response_dns.json()} + + return response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipinfo.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipinfo.py new file mode 100644 index 0000000..abae077 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipinfo.py @@ -0,0 +1,43 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class IPInfo(classes.ObservableAnalyzer): + url: str = "https://ipinfo.io/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + try: + response = requests.get( + self.url + self.observable_name, + params={"token": self._api_key_name}, + ) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipqs.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipqs.py new file mode 100644 index 0000000..6a2a7a0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ipqs.py @@ -0,0 +1,194 @@ +import logging +import re + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +IP_REG = ( + "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}" + "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$" +) +IPv6_REG = ( + r"\b(?:(?:[0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|" + r"(?:[0-9a-fA-F]{1,4}:){1,7}:|" + r"(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|" + r"(?:[0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|" + r"(?:[0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|" + r"(?:[0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|" + r"(?:[0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|" + r"[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:" + r"(?:(:[0-9a-fA-F]{1,4}){1,7}|:)|" + r"fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|" + r"::(ffff(:0{1,4}){0,1}:){0,1}" + r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}" + r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|" + r"([0-9a-fA-F]{1,4}:){1,4}:" + r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}" + r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\b" +) +EMAIL_REG = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}" +DOMAIN_REG = re.compile( + r"^(?:[a-zA-Z0-9]" # First character of the domain + r"(?:[a-zA-Z0-9-_]{0,61}[A-Za-z0-9])?\.)" # Sub domain + hostname + r"+[A-Za-z0-9][A-Za-z0-9-_]{0,61}" # First 61 characters of the gTLD + r"[A-Za-z]$" # Last character of the gTLD +) +PHONE_REG = "^\+?[1-9]\d{1,14}$" +URL_REG = ( + "((http|https)://)(www.)?[a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b" + "([-a-zA-Z0-9@:%._\\+~#?&//=]*)" +) + + +class IPQualityScore(classes.ObservableAnalyzer): + _ipqs_api_key: str + url_timeout: int = 2 + url_strictness: int = 0 + url_fast: bool = False + phone_strictness: int = 0 + enhanced_name_check: bool = False + enhanced_line_check: bool = False + country: str = "" + user_language: str = None + user_agent: str = None + transaction_strictness: int = 0 + ip_strictness: int = 0 + mobile: bool = False + lighter_penalties: bool = False + ip_fast: bool = True + allow_public_access_points: bool = True + email_timeout: int = 7 + suggest_domain: bool = False + email_strictness: int = 0 + email_fast: bool = True + abuse_strictness: int = 0 + + IPQS_BASE_URL = "https://ipqualityscore.com/api/json/" + URL_ENDPOINT = IPQS_BASE_URL + "url?url=" + IP_ENDPOINT = IPQS_BASE_URL + "ip?ip=" + EMAIL_ENDPOINT = IPQS_BASE_URL + "email?email=" + PHONE_ENDPOINT = IPQS_BASE_URL + "phone?phone=" + + def _get_url_payload(self): + return { + "strictness": self.url_strictness, + "fast": str(self.url_fast).lower(), + "timeout": self.url_timeout, + } + + def _get_ip_payload(self): + payload = { + "strictness": self.ip_strictness, + "allow_public_access_points": str(self.allow_public_access_points).lower(), + "fast": str(self.ip_fast).lower(), + "lighter_penalties": str(self.lighter_penalties).lower(), + "mobile": str(self.mobile).lower(), + "transaction_strictness": self.transaction_strictness, + } + if self.user_agent: + payload["user_agent"] = self.user_agent + if self.user_language: + payload["user_language"] = self.user_language + return payload + + def _get_email_payload(self): + return { + "fast": str(self.email_fast).lower(), + "timeout": self.email_timeout, + "suggest_domain": str(self.suggest_domain).lower(), + "strictness": self.email_strictness, + "abuse_strictness": self.abuse_strictness, + } + + def _get_phone_payload(self): + return { + "strictness": self.phone_strictness, + "country": [self.country], + "enhanced_line_check": str(self.enhanced_line_check).lower(), + "enhanced_name_check": str(self.enhanced_name_check).lower(), + } + + def _get_calling_endpoint(self): + if re.match(IP_REG, self.observable_name) or re.match( + IPv6_REG, self.observable_name + ): + return self.IP_ENDPOINT, self._get_ip_payload() + elif re.match(DOMAIN_REG, self.observable_name) or re.match( + URL_REG, self.observable_name + ): + return self.URL_ENDPOINT, self._get_url_payload() + elif re.match(EMAIL_REG, self.observable_name): + return self.EMAIL_ENDPOINT, self._get_email_payload() + elif re.match(PHONE_REG, self.observable_name): + return self.PHONE_ENDPOINT, self._get_phone_payload() + else: + return None, None + + def run(self): + calling_endpoint, payload = self._get_calling_endpoint() + ipqs_headers = {"IPQS-KEY": self._ipqs_api_key} + + try: + if calling_endpoint and payload is not None: + response = requests.get( + calling_endpoint + self.observable_name, + headers=ipqs_headers, + params=payload, + ) + response.raise_for_status() + result = response.json() + return result + else: + logger.warning("Invalid or unsupported observable type") + raise AnalyzerRunException("Invalid or unsupported observable type") + except requests.RequestException as e: + raise AnalyzerRunException(e) + + @classmethod + def _monkeypatch(cls): + sample_response = { + "message": "Success.", + "success": True, + "unsafe": False, + "domain": "test.com", + "ip_address": "0.0.0.0", + "server": "gws", + "content_type": "text/html; charset=UTF-8", + "status_code": 200, + "page_size": 82252, + "domain_rank": 1, + "dns_valid": True, + "parking": False, + "spamming": False, + "malware": False, + "phishing": False, + "suspicious": False, + "adult": False, + "risk_score": 0, + "country_code": "US", + "category": "Search Engines", + "domain_age": { + "human": "26 years ago", + "timestamp": 874296000, + "iso": "1997-09-15T00:00:00-04:00", + }, + "redirected": False, + "language_code": "N/A", + "final_url": "http://test.com", + "request_id": "KWc8M5Dvep", + } + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse(sample_response, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/koodous.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/koodous.py new file mode 100644 index 0000000..8fcc6a0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/koodous.py @@ -0,0 +1,51 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Koodous(classes.ObservableAnalyzer): + url: str = "https://developer.koodous.com/apks/" + query_analysis = "/analysis" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def get_response(self, url): + return requests.get( + url, headers={"Authorization": f"Token {self._api_key_name}"} + ) + + def run(self): + common_url = self.url + self.observable_name + + apk_info = self.get_response(common_url) + apk_info.raise_for_status() + + apk_analysis = self.get_response(common_url + self.query_analysis) + apk_analysis.raise_for_status() + + response = { + "apk_info": apk_info.json(), + "analysis_report": apk_analysis.json(), + } + + return response + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/maxmind.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/maxmind.py new file mode 100644 index 0000000..639acf1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/maxmind.py @@ -0,0 +1,230 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import datetime +import logging +import os +import shutil +import tarfile + +import maxminddb +import requests +from django.conf import settings +from geoip2.database import Reader +from geoip2.errors import AddressNotFoundError, GeoIP2Error +from geoip2.models import ASN, City, Country + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from api_app.models import PluginConfig +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class MaxmindDBManager: + _supported_dbs: [str] = ["GeoLite2-Country", "GeoLite2-City", "GeoLite2-ASN"] + _default_db_extension: str = ".mmdb" + + @classmethod + def get_supported_dbs(cls) -> [str]: + return [db_name + cls._default_db_extension for db_name in cls._supported_dbs] + + @classmethod + def update_all_dbs(cls, api_key: str) -> bool: + return all(cls._update_db(db, api_key) for db in cls._supported_dbs) + + def query_all_dbs(self, observable_query: str, api_key: str) -> (dict, dict): + maxmind_final_result: {} = {} + maxmind_errors: [] = [] + for db in self._supported_dbs: + maxmind_result, maxmind_error = self._query_single_db( + observable_query, db, api_key + ) + + if maxmind_error: + maxmind_errors.append(maxmind_error["error"]) + elif maxmind_result: + logger.info(f"maxmind result: {maxmind_result} in {db=}") + maxmind_final_result.update(maxmind_result) + else: + logger.warning(f"maxmind result not available in {db=}") + + return maxmind_final_result, maxmind_errors + + @classmethod + def _get_physical_location(cls, db: str) -> str: + return f"{settings.MEDIA_ROOT}/{db}{cls._default_db_extension}" + + def _query_single_db( + self, query_ip: str, db_name: str, api_key: str + ) -> (dict, dict): + result: ASN | City | Country + db_path: str = self._get_physical_location(db_name) + self._check_and_update_db(api_key, db_name) + + logger.info(f"Query {db_name=} for {query_ip=}") + with Reader(db_path) as reader: + try: + if "ASN" in db_name: + result = reader.asn(query_ip) + elif "Country" in db_name: + result = reader.country(query_ip) + elif "City" in db_name: + result = reader.city(query_ip) + except AddressNotFoundError: + reader.close() + logger.info( + f"Query for observable '{query_ip}' " + "didn't produce any results in any db." + ) + return {}, {} + except (GeoIP2Error, maxminddb.InvalidDatabaseError) as e: + error_message = f"GeoIP2 database error: {e}" + logger.exception(error_message) + return {}, {"error": error_message} + else: + reader.close() + return result.raw, {} + + def _check_and_update_db(self, api_key: str, db_name: str): + db_path = self._get_physical_location(db_name) + if not os.path.isfile(db_path) and not self._update_db(db_name, api_key): + raise AnalyzerRunException( + f"failed extraction of maxmind db {db_name}," + " reached max number of attempts" + ) + if not os.path.exists(db_path): + raise maxminddb.InvalidDatabaseError( + f"database location '{db_path}' does not exist" + ) + + @classmethod + def _update_db(cls, db: str, api_key: str) -> bool: + if not api_key: + raise AnalyzerConfigurationException( + f"Unable to find api key for {cls.__name__}" + ) + + try: + logger.info(f"starting download of {db=} from maxmind") + + tar_db_path = cls._download_db(db, api_key) + cls._extract_db_to_media_root(tar_db_path) + directory_found = cls._remove_old_db(db) + + if not directory_found: + return False + + logger.info(f"ended download of {db=} from maxmind") + return True + + except Exception as e: + logger.exception(e) + return False + + @classmethod + def _download_db(cls, db_name: str, api_key: str) -> str: + url = ( + "https://download.maxmind.com/app/geoip_download?edition_id=" + f"{db_name}&license_key={api_key}&suffix=tar.gz" + ) + response = requests.get(url) + if response.status_code >= 300: + raise AnalyzerRunException( + f"failed request for new maxmind db {db_name}." + f" Status code: {response.status_code}" + f"\nResponse: {response.raw}" + ) + + return cls._write_db_to_filesystem(db_name, response.content) + + @classmethod + def _write_db_to_filesystem(cls, db_name: str, content: bytes) -> str: + tar_db_path = f"/tmp/{db_name}.tar.gz" + logger.info( + f"starting writing db {db_name} downloaded from maxmind to {tar_db_path}" + ) + with open(tar_db_path, "wb") as f: + f.write(content) + + return tar_db_path + + @classmethod + def _extract_db_to_media_root(cls, tar_db_path: str): + logger.info(f"Started extracting {tar_db_path} to {settings.MEDIA_ROOT}.") + tf = tarfile.open(tar_db_path) + tf.extractall(str(settings.MEDIA_ROOT)) + logger.info(f"Finished extracting {tar_db_path} to {settings.MEDIA_ROOT}.") + + @classmethod + def _remove_old_db(cls, db: str) -> bool: + physical_db_location = cls._get_physical_location(db) + today = datetime.datetime.now().date() + counter = 0 + directory_found = False + # this is because we do not know the exact date of the db we downloaded + while counter < 10 or not directory_found: + formatted_date = (today - datetime.timedelta(days=counter)).strftime( + "%Y%m%d" + ) + downloaded_db_path = ( + f"{settings.MEDIA_ROOT}/" + f"{db}_{formatted_date}/{db}{cls._default_db_extension}" + ) + try: + os.rename(downloaded_db_path, physical_db_location) + except FileNotFoundError: + logger.debug(f"{downloaded_db_path} not found move to the day before") + counter += 1 + else: + directory_found = True + shutil.rmtree(f"{settings.MEDIA_ROOT}/" f"{db}_{formatted_date}") + logger.info(f"maxmind directory found {downloaded_db_path}") + return directory_found + + +class Maxmind(classes.ObservableAnalyzer): + _api_key_name: str + _maxmind_db_manager: "MaxmindDBManager" = MaxmindDBManager() + + def run(self): + maxmind_final_result, maxmind_errors = self._maxmind_db_manager.query_all_dbs( + self.observable_name, self._api_key_name + ) + if maxmind_errors: + for error_msg in maxmind_errors: + self.report.errors.append(error_msg) + self.report.save() + return maxmind_final_result + + @classmethod + def get_db_names(cls) -> [str]: + return cls._maxmind_db_manager.get_supported_dbs() + + @classmethod + def _get_api_key(cls): + for plugin in PluginConfig.objects.filter( + parameter__python_module=cls.python_module, + parameter__is_secret=True, + parameter__name="_api_key_name", + ): + if plugin.value: + return plugin.value + return None + + @classmethod + def update(cls) -> bool: + auth_token = cls._get_api_key() + if auth_token: + return cls._maxmind_db_manager.update_all_dbs(cls._api_key_name) + return False + + @classmethod + def _monkeypatch(cls): + # completely skip because does not work without connection. + patches = [if_mock_connections(patch.object(cls, "run", return_value={}))] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mb_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mb_get.py new file mode 100644 index 0000000..8d9dc5d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mb_get.py @@ -0,0 +1,48 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class MB_GET(classes.ObservableAnalyzer): + url: str = "https://mb-api.abuse.ch/api/v1/" + sample_url: str = "https://bazaar.abuse.ch/sample/" + + def run(self): + return self.query_mb_api(observable_name=self.observable_name) + + @classmethod + def query_mb_api(cls, observable_name: str) -> dict: + """ + This is in a ``classmethod`` so it can be reused in ``MB_GOOGLE``. + """ + post_data = {"query": "get_info", "hash": observable_name} + + response = requests.post(cls.url, data=post_data) + response.raise_for_status() + + result = response.json() + result_data = result.get("data", []) + if result_data and isinstance(result_data, list): + sha256 = result_data[0].get("sha256_hash", "") + if sha256: + result["permalink"] = f"{cls.sample_url}{sha256}" + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse( + {"data": [{"sha256_hash": "test"}]}, 200 + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mb_google.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mb_google.py new file mode 100644 index 0000000..de785d2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mb_google.py @@ -0,0 +1,23 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import googlesearch + +from .mb_get import MB_GET + + +class MB_GOOGLE(MB_GET): + """ + This is a modified version of MB_GET. + """ + + def run(self): + results = {} + + query = f"{self.observable_name} site:bazaar.abuse.ch" + for url in googlesearch.search(query, stop=20): + mb_hash = url.split("/")[-2] + res = super().query_mb_api(observable_name=mb_hash) + results[mb_hash] = res + + return results diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/misp.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/misp.py new file mode 100644 index 0000000..c48b514 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/misp.py @@ -0,0 +1,108 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import datetime + +import pymisp +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockResponseNoOp, if_mock_connections, patch + + +class MISP(classes.ObservableAnalyzer): + _api_key_name: str + _url_key_name: str + + ssl_check: bool + self_signed_certificate: bool + debug: bool + from_days: int + limit: int + enforce_warninglist: bool + filter_on_type: bool + strict_search: bool + timeout: int + published: bool + metadata: bool + + def update(self): + pass + + def run(self): + # this allows self-signed certificates to be used + ssl_param = ( + f"{settings.PROJECT_LOCATION}/configuration/misp_ssl.crt" + if self.ssl_check and self.self_signed_certificate + else self.ssl_check + ) + misp_instance = pymisp.PyMISP( + url=self._url_key_name, + key=self._api_key_name, + ssl=ssl_param, + debug=self.debug, + timeout=self.timeout, + ) + now = datetime.datetime.now() + date_from = now - datetime.timedelta(days=self.from_days) + params = { + "limit": self.limit, + "enforce_warninglist": self.enforce_warninglist, + "published": self.published, + "metadata": self.metadata, + } + + if self.strict_search: + params["value"] = self.observable_name + else: + string_wildcard = f"%{self.observable_name}%" + params["searchall"] = string_wildcard + + if self.from_days != 0: + params["date_from"] = date_from.strftime("%Y-%m-%d %H:%M:%S") + if self.filter_on_type: + params["type_attribute"] = [self.observable_classification] + if self.observable_classification == self.ObservableTypes.HASH: + params["type_attribute"] = ["md5", "sha1", "sha256"] + if self.observable_classification == self.ObservableTypes.IP: + params["type_attribute"] = [ + "ip-dst", + "ip-src", + "ip-src|port", + "ip-dst|port", + "domain|ip", + ] + elif self.observable_classification == self.ObservableTypes.DOMAIN: + params["type_attribute"] = [self.observable_classification, "domain|ip"] + elif self.observable_classification == self.ObservableTypes.HASH: + params["type_attribute"] = ["md5", "sha1", "sha256"] + elif self.observable_classification == self.ObservableTypes.URL: + params["type_attribute"] = [self.observable_classification] + elif self.observable_classification == self.ObservableTypes.GENERIC: + pass + else: + raise AnalyzerConfigurationException( + f"Observable {self.observable_classification} not supported." + "Currently supported are: ip, domain, hash, url, generic." + ) + + result_search = misp_instance.search(**params) + if isinstance(result_search, dict): + errors = result_search.get("errors", []) + if errors: + raise AnalyzerRunException(errors) + + return {"result_search": result_search, "instance_url": self._url_key_name} + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch("pymisp.PyMISP", return_value=MockResponseNoOp({}, 200)), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mmdb_server.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mmdb_server.py new file mode 100644 index 0000000..9a20957 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mmdb_server.py @@ -0,0 +1,79 @@ +import logging + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class MmdbServer(classes.ObservableAnalyzer): + """ + This analyzer is a wrapper for the mmdb-server project. + """ + + def update(self) -> bool: + pass + + url: str + observable_name: str + + def run(self): + response = requests.get(self.url + self.observable_name) + response.raise_for_status() + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + { + "country": {"iso_code": "BE"}, + "meta": { + "description": {"en": "Geo Open MMDB database"}, + "build_db": "2022-02-05 11:37:33", + "db_source": "GeoOpen-Country", + "nb_nodes": 1159974, + }, + "ip": "188.65.220.25", + "country_info": { + "Country": "Belgium", + "Alpha-2 code": "BE", + "Alpha-3 code": "BEL", + "Numeric code": "56", + "Latitude (average)": "50.8333", + "Longitude (average)": "4", + }, + }, + { + "country": { + "iso_code": "BE", + "AutonomousSystemNumber": "49677", + "ASO": "MAEHDROS-AS", + }, + "meta": { + "description": {"en": "Geo Open MMDB database"}, + "build_db": "2022-02-06 10:30:25", + "db_source": "GeoOpen-Country-ASN", + "nb_nodes": 1159815, + }, + "ip": "188.65.220.25", + "country_info": { + "Country": "Belgium", + "Alpha-2 code": "BE", + "Alpha-3 code": "BEL", + "Numeric code": "56", + "Latitude (average)": "50.8333", + "Longitude (average)": "4", + }, + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mnemonic_pdns.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mnemonic_pdns.py new file mode 100644 index 0000000..d92b33a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mnemonic_pdns.py @@ -0,0 +1,48 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class MnemonicPassiveDNS(classes.ObservableAnalyzer): + url: str = "https://api.mnemonic.no/pdns/v3/" + + cof_format: bool + limit: int + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + if self.cof_format: + self.url += "cof/" + response = requests.get( + self.url + self.observable_name, data={"limit": self.limit} + ) + response.raise_for_status() + + if self.cof_format: + result = [json.loads(line) for line in response.text.splitlines()] + + else: + result = response.json() + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mwdb_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mwdb_get.py new file mode 100644 index 0000000..4f20bbc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/mwdb_get.py @@ -0,0 +1,52 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +import mwdblib + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.file_analyzers.mwdb_scan import mocked_mwdb_response +from tests.mock_utils import if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class MWDBGet(ObservableAnalyzer): + _api_key_name: str + + def run(self): + mwdb = mwdblib.MWDB(api_key=self._api_key_name) + + result = {} + try: + file_info = mwdb.query_file(self.observable_name) + except mwdblib.exc.ObjectNotFoundError: + result["not_found"] = True + except Exception as exc: + logger.exception(exc) + self.report.errors.append(str(exc)) + result["not_found"] = True + else: + result["data"] = file_info.data + # this could fail due to non-existing attributes + try: + result["attributes"] = file_info.attributes + except Exception as e: + logger.warning(e, stack_info=True) + self.report.errors.append(str(e)) + result["permalink"] = f"https://mwdb.cert.pl/file/{self.observable_name}" + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "mwdblib.MWDB", + side_effect=mocked_mwdb_response, + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/netlas.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/netlas.py new file mode 100644 index 0000000..c7afe7d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/netlas.py @@ -0,0 +1,110 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Netlas(classes.ObservableAnalyzer): + url: str = "https://app.netlas.io/api/whois_ip/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.query = self.observable_name + + self.headers = {"X-API-Key": f"{self._api_key_name}"} + + self.parameters = {"q": f"ip:{self.query}"} + + def run(self): + try: + response = requests.get( + self.url, params=self.parameters, headers=self.headers + ) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + result = response.json()["items"][0]["data"] + return result + + @classmethod + def _monkeypatch(cls): + example_response = { + "items": [ + { + "data": { + "@timestamp": "2023-07-06T14:53:32", + "ip": {"gte": "8.8.8.0", "lte": "8.8.8.255"}, + "related_nets": [ + { + "country": "US", + "address": "1600 Amphitheatre Parkway", + "city": "Mountain View", + "created": "2014-03-14", + "range": "8.8.8.0 - 8.8.8.255", + "description": "Google LLC", + "handle": "NET-8-8-8-0-1", + "organization": "Google LLC (GOGL)", + "name": "LVLT-GOGL-8-8-8", + "start_ip": "8.8.8.0", + "cidr": ["8.8.8.0/24"], + "net_size": 255, + "state": "CA", + "postal_code": "94043", + "updated": "2014-03-14", + "end_ip": "8.8.8.255", + } + ], + "net": { + "country": "US", + "address": "100 CenturyLink Drive", + "city": "Monroe", + "created": "1992-12-01", + "range": "8.0.0.0 - 8.127.255.255", + "description": "Level 3 Parent, LLC", + "handle": "NET-8-0-0-0-1", + "organization": "Level 3 Parent, LLC (LPL-141)", + "name": "LVLT-ORG-8-8", + "start_ip": "8.0.0.0", + "cidr": ["8.0.0.0/9"], + "net_size": 8388607, + "state": "LA", + "postal_code": "71203", + "updated": "2018-04-23", + "end_ip": "8.127.255.255", + }, + "asn": { + "number": ["15169"], + "registry": "arin", + "country": "US", + "name": "GOOGLE", + "cidr": "8.8.8.0/24", + "updated": "1992-12-01", + }, + } + } + ], + "took": 8, + "timestamp": 1691652090, + } + + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse(example_response, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/onionscan.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/onionscan.py new file mode 100644 index 0000000..b37111a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/onionscan.py @@ -0,0 +1,31 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, ObservableAnalyzer + + +class Onionscan(ObservableAnalyzer, DockerBasedAnalyzer): + name: str = "Onionscan" + url: str = "http://tor_analyzers:4001/onionscan" + # http request polling max number of tries + max_tries: int = 60 + # interval between http request polling (in seconds) + poll_distance: int = 10 + verbose: bool + tor_proxy_address: str + + def run(self): + # make request params + args = [] + if self.verbose: + args.append("-verbose") + if self.tor_proxy_address: + args.extend(["-torProxyAddress", self.tor_proxy_address]) + # make request data + args.extend(["-jsonReport", self.observable_name]) + + req_data = { + "args": args, + } + + return self._docker_run(req_data=req_data, req_files=None) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/onyphe.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/onyphe.py new file mode 100644 index 0000000..d6f08d3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/onyphe.py @@ -0,0 +1,57 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Onyphe(classes.ObservableAnalyzer): + url: str = "https://www.onyphe.io/api/v2/summary/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = { + "Authorization": f"apikey {self._api_key_name}", + "Content-Type": "application/json", + } + obs_clsfn = self.observable_classification + + if obs_clsfn == self.ObservableTypes.DOMAIN: + uri = f"domain/{self.observable_name}" + elif obs_clsfn == self.ObservableTypes.IP: + uri = f"ip/{self.observable_name}" + elif obs_clsfn == self.ObservableTypes.URL: + uri = f"hostname/{self.observable_name}" + else: + raise AnalyzerRunException( + f"not supported observable type {obs_clsfn}." + " Supported are: ip, domain and url." + ) + + try: + response = requests.get(self.url + uri, headers=headers) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/opencti.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/opencti.py new file mode 100644 index 0000000..fd8e708 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/opencti.py @@ -0,0 +1,94 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import pycti +from pycti.api.opencti_api_client import File + +from api_app.analyzers_manager import classes +from tests.mock_utils import if_mock_connections, patch + +# for lighter output (credits: Cortex-Analyzers/opencti) +RESULT_TRIM_MAP = { + "observable": [ + "objectMarkingIds", + "objectLabelIds", + "externalReferencesIds", + "indicatorsIds", + "parent_types", + ], + "report": { + "objects", + "objectMarkingIds", + "externalReferencesIds", + "objectLabelIds", + "parent_types", + "objectsIds", + "x_opencti_graph_data", + }, +} + + +class OpenCTI(classes.ObservableAnalyzer): + ssl_verify: bool + proxies: dict + exact_search: bool + _url_key_name: str + _api_key_name: str + + def run(self): + # set up client + opencti_instance = pycti.OpenCTIApiClient( + url=self._url_key_name, + token=self._api_key_name, + ssl_verify=self.ssl_verify, + proxies=self.proxies, + ) + + # search for observables + observables = pycti.StixCyberObservable(opencti_instance, File).list( + search=self._job.observable_name + ) + + # Filter exact matches if exact_search is set + if self.exact_search: + observables = [ + obs + for obs in observables + if obs["observable_value"] == self._job.observable_name + ] + + for observable in observables: + # get reports linked to this observable + reports = pycti.Report(opencti_instance).list( + filters=[ + { + "key": "objectContains", + "values": [observable["id"]], + } + ] + ) + # trim observable data + for key in RESULT_TRIM_MAP["observable"]: + observable.pop(key, None) + for report in reports: + # trim report data + for key in RESULT_TRIM_MAP["report"]: + report.pop(key, None) + + observable["reports"] = reports + + return observables + + @classmethod + def _monkeypatch(cls): + mock_obs = [{"id": 1, "observable_value": "8.8.8.8", "objectMarkingIds": []}] + mock_report = [{"id": 1, "observable_value": "8.8.8.8", "objects": []}] + + patches = [ + if_mock_connections( + patch("pycti.OpenCTIApiClient", return_value=None), + patch("pycti.StixCyberObservable.list", return_value=mock_obs), + patch("pycti.Report.list", return_value=mock_report), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/otx.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/otx.py new file mode 100644 index 0000000..bd325b7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/otx.py @@ -0,0 +1,221 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging +from ipaddress import AddressValueError, IPv4Address +from typing import List +from urllib.parse import urlparse + +import OTXv2 +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from api_app.helpers import get_hash_type +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class OTXv2Extended(OTXv2.OTXv2): + """ + This is to add "timeout" feature without having to do a fork + Once this PR is merged: https://github.com/AlienVault-OTX/OTX-Python-SDK/pull/66 + we can remove this and use the upstream + """ + + def __init__(self, *args, timeout=None, **kwargs): + super().__init__(*args, **kwargs) + self.timeout = timeout + + def session(self): + # modified version where retries are not implemented. + # this was needed because, otherwise, the analyzer could last too much time + # and become the bottleneck of all the application + if self.request_session is None: + self.request_session = requests.Session() + + return self.request_session + + def get(self, url, **kwargs): + try: + response = self.session().get( + self.create_url(url, **kwargs), + headers=self.headers, + proxies=self.proxies, + verify=self.verify, + cert=self.cert, + timeout=self.timeout, + ) + return self.handle_response_errors(response).json() + except ( + OTXv2.requests.exceptions.RetryError, + OTXv2.requests.exceptions.Timeout, + ) as e: + raise OTXv2.RetryError(e) + + +class OTX(classes.ObservableAnalyzer): + """This class use an OTX API to download data about an observable. + Observable's data are divided into sections: + It's possible to download only some sections to reduce the wait time: + Download all the data is slow with the IP addresses. + """ + + verbose: bool + sections: list + full_analysis: bool + timeout: int = 30 + + _api_key_name: str + + def _extract_indicator_type(self) -> "OTXv2.IndicatorTypes": + observable_classification = self.observable_classification + if observable_classification == self.ObservableTypes.IP: + otx_type = OTXv2.IndicatorTypes.IPv4 + elif observable_classification == self.ObservableTypes.URL: + to_analyze_observable = urlparse(self.observable_name).hostname + + try: + to_analyze_observable = IPv4Address(to_analyze_observable) + except AddressValueError: + otx_type = OTXv2.IndicatorTypes.DOMAIN + else: + otx_type = OTXv2.IndicatorTypes.IPv4 + + if not to_analyze_observable: + raise AnalyzerRunException("extracted observable is None") + elif observable_classification == self.ObservableTypes.DOMAIN: + otx_type = OTXv2.IndicatorTypes.DOMAIN + elif observable_classification == self.ObservableTypes.HASH: + matched_type = get_hash_type(self.observable_name) + if matched_type == "md5": + otx_type = OTXv2.IndicatorTypes.FILE_HASH_MD5 + elif matched_type == "sha-1": + otx_type = OTXv2.IndicatorTypes.FILE_HASH_SHA1 + elif matched_type == "sha-256": + otx_type = OTXv2.IndicatorTypes.FILE_HASH_SHA256 + else: + raise AnalyzerRunException(f"hash {matched_type} not supported") + else: + raise AnalyzerRunException( + f"not supported observable classification {observable_classification}" + ) + return otx_type + + def _extract_pulses(self, general_data: dict) -> List[dict]: + pulse_list = general_data.get("pulse_info", {}).get("pulses", []) + # for some observables the output could really be overwhelming + if not self.verbose and pulse_list: + pulse_list = pulse_list[:20] + for pulse in pulse_list: + pulse_id = pulse.get("id", "") + if pulse_id: + pulse["link"] = f"https://otx.alienvault.com/pulse/{pulse_id}" + return pulse_list + + @classmethod + def _extract_geo(cls, geo_data: dict) -> dict: + return geo_data + + @classmethod + def _extract_malware_samples(cls, malware_data: dict) -> List[dict]: + return [ + {"hash": sample.get("hash", ""), "detections": sample.get("detections", {})} + for sample in malware_data.get("data", []) + ] + + @classmethod + def _extract_passive_dns(cls, passive_dns_data: dict) -> List[dict]: + return passive_dns_data.get("passive_dns", []) + + @classmethod + def _extract_reputation(cls, reputation_data: dict): + return reputation_data.get("reputation", None) + + @classmethod + def _extract_url_list(cls, url_list_data: dict) -> List[dict]: + return url_list_data.get("url_list", []) + + def _extract_analysis(self, analysis_data: dict) -> dict: + analysis_result = analysis_data.get("analysis", {}) + if not self.verbose and analysis_result and "plugins" in analysis_result: + analysis_result["plugins"] = "removed because too long" + return analysis_result + + def run(self): + otx = OTXv2Extended( + timeout=self.timeout, api_key=self._api_key_name, user_agent="IntelOwl" + ) + + to_analyze_observable = self.observable_name + otx_type = self._extract_indicator_type() + + if self.full_analysis: + self.sections = otx_type.sections + + # check if all requested sections are available for the observable type + not_supported_requested_section_list = list( + filter( + lambda requested_section: requested_section not in otx_type.sections, + self.sections, + ) + ) + if not_supported_requested_section_list: + logger.warning( + f"Sections: {not_supported_requested_section_list}" + f" are not supported for indicator type: {otx_type}. " + "We remove them from the search." + ) + for not_supported in not_supported_requested_section_list: + self.sections.remove(not_supported) + + result = {} + for section in self.sections: + logger.info( + "requesting OTX info for indicator " + f"{to_analyze_observable} and section {section}" + ) + try: + details = otx.get_indicator_details_by_section( + indicator_type=otx_type, + indicator=to_analyze_observable, + section=section, + ) + except (OTXv2.BadRequest, OTXv2.RetryError) as e: + raise AnalyzerRunException(f"Error while requesting data to OTX: {e}") + except OTXv2.NotFound as e: + logger.info(f"{to_analyze_observable} not found: {e}") + else: + # This mapping is used to avoid a verbose elif structure: + # Each keyword is mapped in a tuple with the logic to extract the data + # and the name of the output field + section_extractor_mapping = { + "general": (self._extract_pulses, "pulses"), + "geo": (OTX._extract_geo, "geo"), + "malware": (OTX._extract_malware_samples, "malware_samples"), + "passive_dns": (OTX._extract_passive_dns, "passive_dns"), + "reputation": (OTX._extract_reputation, "reputation"), + "url_list": (OTX._extract_url_list, "url_list"), + "analysis": (self._extract_analysis, "analysis"), + } + logger.debug(f"OTX raw data: {details}") + # get the function and the label related to the section + selected_section_config = section_extractor_mapping[section] + data = selected_section_config[0](details) + field_name = selected_section_config[1] + logger.debug( + f"observable {to_analyze_observable} extracted data: {data}," + f" field name: {field_name}" + ) + result[field_name] = data + logger.debug(f"result: {result}") + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch("requests.Session.get", return_value=MockUpResponse({}, 200)) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishing_army.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishing_army.py new file mode 100644 index 0000000..05329a0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishing_army.py @@ -0,0 +1,80 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import os +from urllib.parse import urlparse + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +db_name = "phishing_army.txt" +database_location = f"{settings.MEDIA_ROOT}/{db_name}" + + +class PhishingArmy(classes.ObservableAnalyzer): + url = "https://phishing.army/download/phishing_army_blocklist.txt" + + def run(self): + result = {"found": False} + if not os.path.isfile(database_location): + if not self.update(): + raise AnalyzerRunException("Failed extraction of Phishing Army db") + + if not os.path.exists(database_location): + raise AnalyzerRunException( + f"database location {database_location} does not exist" + ) + + with open(database_location, "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + to_analyze_observable = self.observable_name + if self.observable_classification == self.ObservableTypes.URL: + to_analyze_observable = urlparse(self.observable_name).hostname + + if to_analyze_observable in db_list: + result["found"] = True + + result["link"] = self.url + + return result + + @classmethod + def update(cls): + try: + logger.info("starting download of db from Phishing Army") + r = requests.get(cls.url) + r.raise_for_status() + + with open(database_location, "w", encoding="utf-8") as f: + f.write(r.content.decode()) + + if not os.path.exists(database_location): + return False + + logger.info("ended download of db from Phishing Army") + return True + except Exception as e: + logger.exception(e) + + return False + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200, content=b"91.192.100.61"), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishstats.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishstats.py new file mode 100644 index 0000000..5ee7b77 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishstats.py @@ -0,0 +1,73 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from ipaddress import AddressValueError, IPv4Address +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class PhishStats(ObservableAnalyzer): + """ + Analyzer that uses PhishStats API to check if the observable is a phishing site. + """ + + url: str = "https://phishstats.info:2096/api" + + @classmethod + def update(cls) -> bool: + pass + + def __build_phishstats_url(self) -> str: + to_analyze_observable_classification = self.observable_classification + to_analyze_observable_name = self.observable_name + if self.observable_classification == self.ObservableTypes.URL: + to_analyze_observable_name = urlparse(self.observable_name).hostname + try: + IPv4Address(to_analyze_observable_name) + except AddressValueError: + to_analyze_observable_classification = self.ObservableTypes.DOMAIN + else: + to_analyze_observable_classification = self.ObservableTypes.IP + + if to_analyze_observable_classification == self.ObservableTypes.IP: + endpoint = ( + f"phishing?_where=(ip,eq,{to_analyze_observable_name})&_sort=-date" + ) + elif to_analyze_observable_classification == self.ObservableTypes.DOMAIN: + endpoint = ( + f"phishing?_where=(url,like,~{to_analyze_observable_name}~)&_sort=-date" + ) + elif to_analyze_observable_classification == self.ObservableTypes.GENERIC: + endpoint = ( + "phishing?_where=(title,like," + f"~{to_analyze_observable_name}~)&_sort=-date" + ) + else: + raise AnalyzerRunException( + "Phishstats require either of IP, URL, Domain or Generic" + ) + return f"{self.url}/{endpoint}" + + def run(self): + api_url = self.__build_phishstats_url() + response = requests.get(api_url) + response.raise_for_status() + + return {"api_url": api_url, "results": response.json()} + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishtank.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishtank.py new file mode 100644 index 0000000..abd88ef --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phishtank.py @@ -0,0 +1,57 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import base64 +import logging +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Phishtank(ObservableAnalyzer): + _api_key_name: str + + def run(self): + headers = {"User-Agent": "phishtank/IntelOwl"} + observable_to_analyze = self.observable_name + if self.observable_classification == self.ObservableTypes.DOMAIN: + observable_to_analyze = "http://" + self.observable_name + parsed = urlparse(observable_to_analyze) + if not parsed.path: + observable_to_analyze += "/" + data = { + "url": base64.b64encode(observable_to_analyze.encode("utf-8")), + "format": "json", + } + # optional API key + if not hasattr(self, "_api_key_name"): + logger.warning(f"{self.__repr__()} -> Continuing w/o API key..") + else: + data["app_key"] = self._api_key_name + try: + resp = requests.post( + "https://checkurl.phishtank.com/checkurl/", data=data, headers=headers + ) + resp.raise_for_status() + result = resp.json() + except requests.RequestException as e: + raise AnalyzerRunException(e) + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phoneinfoga_scan.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phoneinfoga_scan.py new file mode 100644 index 0000000..b1ec9dd --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/phoneinfoga_scan.py @@ -0,0 +1,108 @@ +import logging +from enum import Enum +from typing import Dict, List + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerConfigurationException +from tests.mock_utils import MockUpResponse + +logger = logging.getLogger(__name__) + + +class SCANNER_NAMES(Enum): + LOCAL = "local" + NUM_VERIFY = "numverify" + GOOGLECSE = "googlecse" + OVH = "ovh" + + @classmethod + def values(cls): + return list(map(lambda c: c.value, cls)) + + +class Phoneinfoga(classes.ObservableAnalyzer, classes.DockerBasedAnalyzer): + """ + Docker based analyzer for phoneinfoga + """ + + def update(self) -> bool: + pass + + observable_name: str + scanners: List[str] + all_scanners: bool + googlecse_max_results: int = 10 + name: str = "phoneinfoga" + # here is a list of pre declared api keys, user can put + # values as per their required scanner, by default it is null + + _NUMVERIFY_API_KEY: str = "" + _GOOGLECSE_CX: str = "" + _GOOGLE_API_KEY: str = "" + + url = "http://phoneinfoga:5000" + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self.all_scanners: + self.scanners = SCANNER_NAMES.values() + else: + for scanner in self.scanners: + if scanner not in SCANNER_NAMES.values(): + raise AnalyzerConfigurationException( + f"Scanner {scanner} not supported." + f" Choices are {', '.join(SCANNER_NAMES.values())}" + ) + + def run(self): + result = {} + for scanner in self.scanners: + try: + url: str = f"{self.url}/api/v2/scanners/{scanner}/run" + options = {} + if scanner == SCANNER_NAMES.NUM_VERIFY.value: + options["NUMVERIFY_API_KEY"] = self._NUMVERIFY_API_KEY + elif scanner == SCANNER_NAMES.GOOGLECSE.value: + options = { + "GOOGLECSE_CX": self._GOOGLECSE_CX, + "GOOGLE_API_KEY": self._GOOGLE_API_KEY, + "GOOGLECSE_MAX_RESULTS": self.googlecse_max_results, + } + response = requests.post( + url, + headers={ + "Content-Type": "application/json", + "accept": "application/json", + }, + json={"number": self.observable_name, "options": options}, + ) + response.raise_for_status() + result[scanner] = response.json() + except requests.RequestException as e: + if scanner == "ovh": + logger.info(f"ovh scanner seems not working. {e}", stack_info=True) + else: + logger.error(e, stack_info=True) + self.report.errors.append(str(e)) + + return result + + @staticmethod + def mocked_docker_analyzer_post(*args, **kwargs): + mockrespose = { + "result": { + "valid": True, + "number": "33679368229", + "local_format": "0679368229", + "international_format": "+33679368229", + "country_prefix": "+33", + "country_code": "FR", + "country_name": "France", + "location": "", + "carrier": "Orange France SA", + "line_type": "mobile", + } + } + return MockUpResponse(mockrespose, 200) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/pulsedive.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/pulsedive.py new file mode 100644 index 0000000..ae6c6ba --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/pulsedive.py @@ -0,0 +1,136 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Pulsedive(ObservableAnalyzer): + url: str = "https://pulsedive.com/api" + max_tries: int = 10 + poll_distance: int = 10 + + scan_mode: str + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + supported_scan_values = ["basic", "passive", "active"] + if self.scan_mode not in supported_scan_values: + raise AnalyzerConfigurationException( + "scan_mode is not a supported value." + f" Supported are {supported_scan_values}" + ) + self.probe = 1 if self.scan_mode == "active" else 0 # else is "passive" + + def run(self): + result = {} + self.default_param = "" + # optional API key + if not hasattr(self, "_api_key_name"): + warning = "No API key retrieved" + logger.info( + f"{warning}. Continuing without API key..." f" <- {self.__repr__()}" + ) + self.report.errors.append(warning) + else: + self.default_param = f"&key={self._api_key_name}" + + # headers = {"Key": api_key, "Accept": "application/json"} + # 1. query to info.php to check if the indicator is already in the database + params = f"indicator={self.observable_name}" + if hasattr(self, "_api_key_name"): + params += self.default_param + resp = requests.get(f"{self.url}/info.php?{params}") + + # handle 404 case, submit for analysis + if resp.status_code == 404 and self.scan_mode != "basic": + # 2. submit new scan and then poll for result + result = self.__submit_for_analysis() + else: + resp.raise_for_status() + result = resp.json() + + return result + + def __submit_for_analysis(self) -> dict: + params = f"value={self.observable_name}&probe={self.probe}" + if hasattr(self, "_api_key_name"): + params += self.default_param + headers = {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + resp = requests.post(f"{self.url}/analyze.php", data=params, headers=headers) + resp.raise_for_status() + qid = resp.json().get("qid", None) + # 3. retrieve result using qid after waiting for 10 seconds + params = f"qid={qid}" + if hasattr(self, "_api_key_name"): + params += self.default_param + result = self.__poll_for_result(params) + if result.get("data", None): + result = result["data"] + + return result + + def __poll_for_result(self, params): + result = {} + url = f"{self.url}/analyze.php?{params}" + obj_repr = self.__repr__() + for chance in range(self.max_tries): + logger.info( + f"polling request #{chance+1} for observable: {self.observable_name}" + f" <- {obj_repr}" + ) + time.sleep(self.poll_distance) + resp = requests.get(url) + resp.raise_for_status() + resp_json = resp.json() + status = resp_json.get("status", None) + if status == "done": + result = resp_json + break + elif status == "processing": + continue + else: + err = resp_json.get("error", "Report not found.") + raise AnalyzerRunException(err) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + side_effect=[ + MockUpResponse( + {}, 404 + ), # 404 so `__submit_for_analysis` is called + MockUpResponse( + {"status": "done", "data": {"test": "test"}}, 200 + ), + ], + ), + patch( + "requests.post", + side_effect=lambda *args, **kwargs: MockUpResponse({"qid": 1}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/robtex.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/robtex.py new file mode 100644 index 0000000..9c5458a --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/robtex.py @@ -0,0 +1,64 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Robtex(classes.ObservableAnalyzer): + url = "https://freeapi.robtex.com/" + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + if self.observable_classification == self.ObservableTypes.IP: + uris = [ + f"ipquery/{self.observable_name}", + f"pdns/reverse/{self.observable_name}", + ] + elif self.observable_classification in [ + self.ObservableTypes.URL, + self.ObservableTypes.DOMAIN, + ]: + if self.observable_classification == self.ObservableTypes.URL: + domain = urlparse(self.observable_name).hostname + else: + domain = self.observable_name + uris = [f"pdns/forward/{domain}"] + else: + raise AnalyzerRunException( + f"not supported analysis type {self.observable_classification}." + ) + + loaded_results = [] + for uri in uris: + response = requests.get(self.url + uri) + response.raise_for_status() + result = response.text.split("\r\n") + for item in result: + if len(item) > 0: + loaded_results.append(json.loads(item)) + + return loaded_results + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + {}, 200, text='{"test1":"test1"}\r\n{"test2":"test2"}' + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/securitytrails.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/securitytrails.py new file mode 100644 index 0000000..fb13535 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/securitytrails.py @@ -0,0 +1,78 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class SecurityTrails(classes.ObservableAnalyzer): + url: str = "https://api.securitytrails.com/v1/" + securitytrails_analysis: str + securitytrails_current_type: str + securitytrails_history_analysis: str + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = {"apikey": self._api_key_name, "Content-Type": "application/json"} + + if self.observable_classification == self.ObservableTypes.IP: + uri = f"ips/nearby/{self.observable_name}" + elif self.observable_classification == self.ObservableTypes.DOMAIN: + if self.securitytrails_analysis == "current": + if self.securitytrails_current_type == "details": + uri = f"domain/{self.observable_name}" + elif self.securitytrails_current_type == "subdomains": + uri = f"domain/{self.observable_name}/subdomains" + elif self.securitytrails_current_type == "tags": + uri = f"domain/{self.observable_name}/tags" + else: + raise AnalyzerRunException( + "Not supported endpoint for current analysis." + ) + + elif self.securitytrails_analysis == "history": + if self.securitytrails_history_analysis == "whois": + uri = f"history/{self.observable_name}/whois" + elif self.securitytrails_history_analysis == "dns": + uri = f"history/{self.observable_name}/dns/a" + else: + raise AnalyzerRunException( + "Not supported endpoint for current analysis." + ) + + else: + raise AnalyzerRunException( + f"Not supported analysis type: {self.securitytrails_analysis}." + ) + else: + raise AnalyzerRunException( + f"Not supported observable type: {self.observable_classification}. " + "Supported are ip and domain." + ) + + try: + response = requests.get(self.url + uri, headers=headers) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/shodan.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/shodan.py new file mode 100644 index 0000000..4090de9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/shodan.py @@ -0,0 +1,60 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Shodan(classes.ObservableAnalyzer): + url: str = "https://api.shodan.io/" + + shodan_analysis: str + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + if self.shodan_analysis == "search": + params = {"key": self._api_key_name, "minify": True} + uri = f"shodan/host/{self.observable_name}" + elif self.shodan_analysis == "honeyscore": + params = { + "key": self._api_key_name, + } + uri = f"labs/honeyscore/{self.observable_name}" + else: + raise AnalyzerConfigurationException( + f"analysis type: '{self.shodan_analysis}' not supported." + "Supported are: 'search', 'honeyscore'." + ) + + try: + response = requests.get(self.url + uri, params=params) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + result = response.json() + if self.shodan_analysis == "honeyscore": + return {"honeyscore": result} + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/spyse.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/spyse.py new file mode 100644 index 0000000..d5bf9e0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/spyse.py @@ -0,0 +1,68 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import re + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from intel_owl.consts import REGEX_CVE, REGEX_EMAIL +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Spyse(classes.ObservableAnalyzer): + url: str = "https://api.spyse.com/v4/data/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def __build_spyse_api_uri(self) -> str: + if self.observable_classification == self.ObservableTypes.DOMAIN: + endpoint = "domain" + elif self.observable_classification == self.ObservableTypes.IP: + endpoint = "ip" + elif self.observable_classification == self.ObservableTypes.GENERIC: + # it may be email + if re.match(REGEX_EMAIL, self.observable_name): + endpoint = "email" + # it may be cve + elif re.match(REGEX_CVE, self.observable_name): + endpoint = "cve" + else: + raise AnalyzerRunException( + f"{self.analyzer_name} with `generic` supports email and CVE only." + ) + else: + raise AnalyzerRunException( + f"{self.observable_classification} not supported." + "Supported are: IP, domain and generic." + ) + return f"{self.url}/{endpoint}/{self.observable_name}" + + def run(self): + headers = { + "Accept": "application/json", + "Authorization": f"Bearer {self._api_key_name}", + } + api_uri = self.__build_spyse_api_uri() + response = requests.get(api_uri, headers=headers) + response.raise_for_status() + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ss_api_net.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ss_api_net.py new file mode 100644 index 0000000..a798064 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/ss_api_net.py @@ -0,0 +1,75 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import base64 + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class SSAPINet(classes.ObservableAnalyzer): + url: str = "https://shot.screenshotapi.net/screenshot" + + _api_key_name: str + use_proxy: bool + proxy: str + output: str + # for other params provided by the API + extra_api_params: dict + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + if self.use_proxy and not self.proxy: + raise AnalyzerConfigurationException( + "No proxy retrieved when use_proxy is true." + ) + if self.output not in ["image", "json"]: + raise AnalyzerConfigurationException( + "output param can only be 'image' or 'json'" + ) + + try: + if isinstance(self.extra_api_params, dict): + params = self.extra_api_params + else: + params = {} + params["url"] = self.observable_name + params["token"] = self._api_key_name + params["output"] = self.output + + if self.use_proxy: + params["proxy"] = self.proxy + + resp = requests.get(self.url, params=params) + resp.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + if self.output == "image": + try: + b64_img = base64.b64encode(resp.content).decode("utf-8") + return {"screenshot": b64_img} + except Exception as err: + raise AnalyzerRunException(f"Failed to convert to base64 string {err}") + return resp.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200, content=b"hello world"), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/stalkphish.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/stalkphish.py new file mode 100644 index 0000000..93b5505 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/stalkphish.py @@ -0,0 +1,59 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Stalkphish(classes.ObservableAnalyzer): + url: str = "https://api.stalkphish.io/api/v1/" + + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = { + "User-Agent": "Stalkphish/IntelOwl", + "Authorization": f"Token {self._api_key_name}", + } + obs_clsfn = self.observable_classification + + if obs_clsfn in [ + self.ObservableTypes.DOMAIN, + self.ObservableTypes.URL, + self.ObservableTypes.GENERIC, + ]: + uri = f"search/url/{self.observable_name}" + elif obs_clsfn == self.ObservableTypes.IP: + uri = f"search/ipv4/{self.observable_name}" + else: + raise AnalyzerRunException( + f"not supported observable type {obs_clsfn}." + " Supported are: ip, domain, url or generic." + ) + + try: + response = requests.get(self.url + uri, headers=headers) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/stratosphere.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/stratosphere.py new file mode 100644 index 0000000..370cdaf --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/stratosphere.py @@ -0,0 +1,131 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import os +from datetime import date, datetime + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +db_name0 = "stratos_ip_blacklist_last24hrs.csv" +db_name1 = "stratos_ip_blacklist_new_attacker.csv" +db_name2 = "stratos_ip_blacklist_repeated_attacker.csv" + +db_loc0 = f"{settings.MEDIA_ROOT}/{db_name0}" +db_loc1 = f"{settings.MEDIA_ROOT}/{db_name1}" +db_loc2 = f"{settings.MEDIA_ROOT}/{db_name2}" + + +class Stratos(classes.ObservableAnalyzer): + @staticmethod + def check_in_list(dataset_loc, ip): + # Checks the IP in a list(S.No,IP,Rating). + with open(dataset_loc, "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + + for ip_tuple in db_list[2:]: + if ip in ip_tuple: + split_tuple = ip_tuple.split(",") + if split_tuple == 3: + ip_rating = (split_tuple[2]).strip() + else: + ip_rating = "found" + return ip_rating + return "" + + def run(self): + ip = self.observable_name + result = { + "last24hrs_rating": "", + "new_attacker_rating": "", + "repeated_attacker_rating": "", + } + + self.check_dataset_status() + + # Checks the IP in last24hrs attacker list. + result["last24hrs_rating"] = self.check_in_list(db_loc0, ip) + # Checks the IP in new attacker list. + result["new_attacker_rating"] = self.check_in_list(db_loc1, ip) + # Checks the IP in repeated attacker list. + result["repeated_attacker_rating"] = self.check_in_list(db_loc2, ip) + + return result + + @staticmethod + def download_dataset(url, db_loc): + # Dataset website certificates are not correctly configured. + p = requests.get(url, verify=False) # lgtm [py/request-without-cert-validation] + p.raise_for_status() + + with open(db_loc, "w", encoding="utf-8") as f: + f.write(p.content.decode()) + + def updater(self): + try: + logger.info("starting download of dataset from stratosphere") + + base_url = "https://mcfp.felk.cvut.cz" + mid_url = "/publicDatasets/CTU-AIPP-BlackList/Todays-Blacklists/" + url0 = base_url + mid_url + "AIP_blacklist_for_IPs_seen_last_24_hours.csv" + priority_url = "AIP_historical_blacklist_prioritized_by_" + url1 = base_url + mid_url + priority_url + "newest_attackers.csv" + url2 = base_url + mid_url + priority_url + "repeated_attackers.csv" + + self.download_dataset(url0, db_loc0) + self.download_dataset(url1, db_loc1) + self.download_dataset(url2, db_loc2) + + if not os.path.exists(db_loc0 or db_loc1 or db_loc2): + raise AnalyzerRunException("failed extraction of stratosphere dataset") + + logger.info("ended download of dataset from stratosphere") + + except Exception as e: + logger.exception(e) + + db_location = [db_loc0, db_loc1, db_loc2] + + return db_location + + def check_dataset_status(self): + if not os.path.isfile(db_loc0 and db_loc1 and db_loc2): + self.updater() + today = date.today() + + timestamp = os.path.getctime(db_loc0) + dt_object = datetime.fromtimestamp(timestamp) + + if ( + dt_object.hour > 3 + and today.day == dt_object.day + and today.month == dt_object.month + and today.year == dt_object.year + ): + logger.info("Dataset is up to date") + else: + os.remove(db_loc0) + os.remove(db_loc1) + os.remove(db_loc2) + self.updater() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200, content=b"7.7.7.7"), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/talos.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/talos.py new file mode 100644 index 0000000..69c2825 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/talos.py @@ -0,0 +1,71 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import os + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +db_name = "talos_ip_blacklist.txt" +database_location = f"{settings.MEDIA_ROOT}/{db_name}" + + +class Talos(classes.ObservableAnalyzer): + def run(self): + result = {"found": False} + if not os.path.isfile(database_location): + if not self.update(): + raise AnalyzerRunException("Failed extraction of talos db") + + if not os.path.exists(database_location): + raise AnalyzerRunException( + f"database location {database_location} does not exist" + ) + + with open(database_location, "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + if self.observable_name in db_list: + result["found"] = True + + return result + + @classmethod + def update(cls) -> bool: + try: + logger.info("starting download of db from talos") + url = "https://snort.org/downloads/ip-block-list" + r = requests.get(url) + r.raise_for_status() + + with open(database_location, "w", encoding="utf-8") as f: + f.write(r.content.decode()) + + if not os.path.exists(database_location): + return False + logger.info("ended download of db from talos") + return True + except Exception as e: + logger.exception(e) + + return False + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200, content=b"91.192.100.61"), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatfox.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatfox.py new file mode 100644 index 0000000..512a1e6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatfox.py @@ -0,0 +1,60 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import json + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class ThreatFox(classes.ObservableAnalyzer): + url: str = "https://threatfox-api.abuse.ch/api/v1/" + disable: bool = False # optional + + def update(self) -> bool: + pass + + def run(self): + if self.disable: + return {"disabled": True} + + payload = {"query": "search_ioc", "search_term": self.observable_name} + + response = requests.post(self.url, data=json.dumps(payload)) + response.raise_for_status() + + result = response.json() + data = result.get("data", []) + if data and isinstance(data, list): + for index, element in enumerate(data): + ioc_id = element.get("id", "") + if ioc_id: + result["data"][index][ + "link" + ] = f"https://threatfox.abuse.ch/ioc/{ioc_id}" + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse( + { + "query_status": "ok", + "data": [ + { + "id": "12", + "ioc": "139.180.203.104:443", + }, + ], + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatminer.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatminer.py new file mode 100644 index 0000000..39b9db9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatminer.py @@ -0,0 +1,54 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Threatminer(classes.ObservableAnalyzer): + url = "https://api.threatminer.org/v2/" + rt_value: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + params = {"q": self.observable_name} + if self.rt_value: + params["rt"] = self.rt_value + + if self.observable_classification == self.ObservableTypes.DOMAIN: + uri = "domain.php" + elif self.observable_classification == self.ObservableTypes.IP: + uri = "host.php" + elif self.observable_classification == self.ObservableTypes.HASH: + uri = "sample.php" + else: + raise AnalyzerRunException( + "Unable to retrieve the uri for classification" + f" {self.observable_classification}" + ) + + try: + response = requests.get(self.url + uri, params=params) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatstream.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatstream.py new file mode 100644 index 0000000..cfef6ce --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/threatstream.py @@ -0,0 +1,84 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Threatstream(classes.ObservableAnalyzer): + url: str = "https://api.threatstream.com/api/" + + threatstream_analysis: str + limit: str + must_active: bool + minimal_confidence: str + modified_after: str + + _api_key_name: str + _api_user_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + params = {} + uri = "" + if self.threatstream_analysis == "intelligence": + self.active = None + if self.must_active: + self.active = "active" + params = { + "value__contains": self.observable_name, + "limit": self.limit, + "status": self.active, + "confidence__gt": self.minimal_confidence, + "modified_ts__gte": self.modified_after, + } # If value = None don't enter in filter + uri = "v2/intelligence/" + elif self.threatstream_analysis == "confidence": + params = {"type": "confidence", "value": self.observable_name} + uri = "v1/inteldetails/confidence_trend/" + elif self.threatstream_analysis == "passive_dns": + if self.observable_classification == self.ObservableTypes.IP: + uri = f"v1/pdns/ip/{self.observable_name}" + elif self.observable_classification == self.ObservableTypes.DOMAIN: + uri = f"v1/pdns/domain/{self.observable_name}" + else: + raise AnalyzerConfigurationException( + f"Observable {self.observable_classification} not supported." + "Currently supported are: ip, domain." + ) + else: + raise AnalyzerConfigurationException( + f"Analysis type: {self.threatstream_analysis} not supported." + "Currently supported are: intelligence, confidence,passive_dns." + ) + try: + api_header = { + "Authorization": f"apikey {self._api_user_name}:{self._api_key_name}" + } + response = requests.get(self.url + uri, params=params, headers=api_header) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/thug_url.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/thug_url.py new file mode 100644 index 0000000..cc9d380 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/thug_url.py @@ -0,0 +1,57 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import secrets + +from api_app.analyzers_manager.classes import DockerBasedAnalyzer, ObservableAnalyzer + + +class ThugUrl(ObservableAnalyzer, DockerBasedAnalyzer): + name: str = "Thug" + url: str = "http://malware_tools_analyzers:4002/thug" + # http request polling max number of tries + max_tries: int = 15 + # interval between http request polling (in seconds) + poll_distance: int = 30 + + user_agent: str + dom_events: str + use_proxy: bool + proxy: str + enable_awis: bool + enable_image_processing_analysis: bool + + def _thug_args_builder(self): + user_agent = self.user_agent + dom_events = self.dom_events + use_proxy = self.use_proxy + proxy = self.proxy + enable_awis = self.enable_awis + enable_img_proc = self.enable_image_processing_analysis + # make request arguments + # analysis timeout is set to 5 minutes + args = ["-T", "300", "-u", str(user_agent)] + if dom_events: + args.extend(["-e", str(dom_events)]) + if use_proxy and proxy: + args.extend(["-p", str(proxy)]) + if enable_awis: + args.append("--awis") + if enable_img_proc: + args.append("--image-processing") + + return args + + def run(self): + args = self._thug_args_builder() + # construct a valid directory name into which thug will save the result + tmp_dir = secrets.token_hex(4) + # make request data + args.extend(["-n", "/home/thug/" + tmp_dir, self.observable_name]) + + req_data = { + "args": args, + "callback_context": {"read_result_from": tmp_dir}, + } + + return self._docker_run(req_data=req_data, req_files=None) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tor.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tor.py new file mode 100644 index 0000000..9cedc11 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tor.py @@ -0,0 +1,84 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import os +import re + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +db_name = "tor_exit_addresses.txt" +database_location = f"{settings.MEDIA_ROOT}/{db_name}" + + +class Tor(classes.ObservableAnalyzer): + def run(self): + result = {"found": False} + if not os.path.isfile(database_location) and not self.update(): + raise AnalyzerRunException("Failed extraction of tor db") + + if not os.path.exists(database_location): + raise AnalyzerRunException( + f"database location {database_location} does not exist" + ) + + with open(database_location, "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + if self.observable_name in db_list: + result["found"] = True + + return result + + @classmethod + def update(cls): + try: + logger.info("starting download of db from tor project") + url = "https://check.torproject.org/exit-addresses" + r = requests.get(url) + r.raise_for_status() + + data_extracted = r.content.decode() + findings = re.findall(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", data_extracted) + + with open(database_location, "w", encoding="utf-8") as f: + for ip in findings: + if ip: + f.write(f"{ip}\n") + + if not os.path.exists(database_location): + return False + + logger.info("ended download of db from tor project") + return True + except Exception as e: + logger.exception(e) + + return False + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + {}, + 200, + content=b"""ExitNode D2A4BEE6754A9711EB0FAC47F3059BE6FC0D72C7 +Published 2022-08-17 18:11:11 +LastStatus 2022-08-18 14:00:00 +ExitAddress 93.95.230.253 2022-08-18 14:44:33""", + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tor_nodes_danmeuk.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tor_nodes_danmeuk.py new file mode 100644 index 0000000..2c767ac --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tor_nodes_danmeuk.py @@ -0,0 +1,87 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import os + +import requests +from django.conf import settings + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + +db_name = "tor_nodes_addresses.txt" +database_location = f"{settings.MEDIA_ROOT}/{db_name}" + + +class TorNodesDanMeUK(classes.ObservableAnalyzer): + def run(self): + result = {"found": False} + if not os.path.isfile(database_location) and not self.update(): + raise AnalyzerRunException("Failed extraction of tor db") + + if not os.path.exists(database_location): + raise AnalyzerRunException( + f"database location {database_location} does not exist" + ) + + with open(database_location, "r", encoding="utf-8") as f: + db = f.read() + + db_list = db.split("\n") + if self.observable_name in db_list: + result["found"] = True + + return result + + @classmethod + def update(cls): + try: + logger.info("starting download of tor nodes from https://dan.me.uk") + url = "https://www.dan.me.uk/torlist/?full" + r = requests.get(url) + r.raise_for_status() + + data_extracted = r.content.decode() + tor_nodes_list = data_extracted.split("\n") + + with open(database_location, "w", encoding="utf-8") as f: + for ip in tor_nodes_list: + if ip: + f.write(f"{ip}\n") + + if not os.path.exists(database_location): + return False + + logger.info("ended download of tor nodes from https://dan.me.uk") + return True + except Exception as e: + logger.exception(e) + + return False + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + {}, + 200, + content=b"""100.10.37.131 +100.14.156.183 +100.16.153.149 +100.4.55.171 +100.8.8.137 +101.100.141.137 +101.55.125.10 +102.119.243.196""", + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tranco.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tranco.py new file mode 100644 index 0000000..f5e3840 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tranco.py @@ -0,0 +1,40 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from urllib.parse import urlparse + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Tranco(classes.ObservableAnalyzer): + url: str = "https://tranco-list.eu/api/ranks/domain/" + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + observable_to_analyze = self.observable_name + if self.observable_classification == self.ObservableTypes.URL: + observable_to_analyze = urlparse(self.observable_name).hostname + + url = self.url + observable_to_analyze + response = requests.get(url) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/triage_base.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/triage_base.py new file mode 100644 index 0000000..7abe370 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/triage_base.py @@ -0,0 +1,89 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from abc import ABCMeta +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import BaseAnalyzerMixin +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) + +logger = logging.getLogger(__name__) + + +class TriageMixin(BaseAnalyzerMixin, metaclass=ABCMeta): + # using public endpoint as the default url + url: str = "https://api.tria.ge/v0/" + private_url: str = "https://private.tria.ge/api/v0/" + report_url: str = "https://tria.ge/" + + endpoint: str + _api_key_name: str + report_type: str + max_tries: int + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self.endpoint == "private": + self.url = self.private_url + + if self.report_type not in ["overview", "complete"]: + raise AnalyzerConfigurationException( + "report_type must be 'overview' or 'complete' " + f"but it is '{self.report_type}'" + ) + self.poll_distance = 3 + self.final_report = {} + self.response = None + + @property + def session(self): + if not hasattr(self, "_session"): + session = requests.Session() + session.headers = { + "Authorization": f"Bearer {self._api_key_name}", + "User-Agent": "IntelOwl", + } + self._session = session + return self._session + + def manage_submission_response(self): + sample_id = self.response.json().get("id", None) + if sample_id is None: + raise AnalyzerRunException("error sending sample") + + self.session.get(self.url + f"samples/{sample_id}/events") + + self.final_report["overview"] = self.get_overview_report(sample_id) + + if self.report_type == "complete": + self.final_report["static_report"] = self.get_static_report(sample_id) + + self.final_report["task_report"] = {} + for task in self.final_report["overview"]["tasks"].keys(): + status_code, task_report_json = self.get_task_report(sample_id, task) + if status_code == 200: + self.final_report["task_report"][f"{task}"] = task_report_json + + analysis_id = self.final_report["overview"].get("sample", {}).get("id", "") + if analysis_id: + self.final_report["permalink"] = f"{self.report_url}{analysis_id}" + + def get_overview_report(self, sample_id): + overview = self.session.get(self.url + f"samples/{sample_id}/overview.json") + return overview.json() + + def get_static_report(self, sample_id): + static = self.session.get(self.url + f"samples/{sample_id}/reports/static") + return static.json() + + def get_task_report(self, sample_id, task): + task_report = self.session.get( + self.url + f"samples/{sample_id}/{task}/report_triage.json" + ) + return task_report.status_code, task_report.json() diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/triage_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/triage_search.py new file mode 100644 index 0000000..3476bd9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/triage/triage_search.py @@ -0,0 +1,90 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.constants import ObservableTypes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from api_app.analyzers_manager.observable_analyzers.triage.triage_base import ( + TriageMixin, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class TriageSearch(ObservableAnalyzer, TriageMixin): + analysis_type: str + + def run(self): + if self.analysis_type == "search": + self.__triage_search() + elif self.analysis_type == "submit": + self.__triage_submit() + else: + raise AnalyzerConfigurationException( + f"analysis type '{self.analysis_type}' not supported." + "Supported are: 'search', 'submit'." + ) + + return self.final_report + + def __triage_search(self): + if self.observable_classification == self.ObservableTypes.HASH: + query = self.observable_name + else: + query = f"{self.observable_classification}:{self.observable_name}" + params = {"query": query} + + self.response = self.session.get(self.url + "search", params=params) + + self.final_report = self.response.json() + + def __triage_submit(self): + data = { + "kind": ObservableTypes.URL, + ObservableTypes.URL: f"{self.observable_name}", + } + + logger.info(f"triage {self.observable_name} sending URL for analysis") + for _try in range(self.max_tries): + logger.info( + f"triage {self.observable_name} polling for result try #{_try + 1}" + ) + self.response = self.session.post(self.url + "samples", json=data) + if self.response.status_code == 200: + break + time.sleep(self.poll_distance) + + if self.response: + self.response.raise_for_status() + self.manage_submission_response() + else: + raise AnalyzerRunException( + f"response not available for {self.observable_name}" + ) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + return_value=MockUpResponse( + {"tasks": {"task_1": {}, "task_2": {}}, "data": []}, 200 + ), + ), + patch( + "requests.Session.post", + return_value=MockUpResponse( + {"id": "sample_id", "status": "pending"}, 200 + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tweetfeeds.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tweetfeeds.py new file mode 100644 index 0000000..8a1cb2b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/tweetfeeds.py @@ -0,0 +1,142 @@ +import json +import logging +import os +from typing import Tuple + +import requests +from django.conf import settings + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class TweetFeeds(ObservableAnalyzer): + """ + Wrapper for https://tweetfeed.live api + """ + + url = "https://api.tweetfeed.live/v1/" + filter1: str = "" + time: str = "" + + @classmethod + def location(cls) -> Tuple[str, str]: + db_name = "tweetfeed_month.json" + url = "https://api.tweetfeed.live/v1/month" + return f"{settings.MEDIA_ROOT}/{db_name}", url + + def run_url(self) -> str: + if self.filter1: + url = ( + self.url + + self.time + + "/" + + self.filter1 + + "/" + + self.observable_classification + ) + else: + url = self.url + self.time + "/" + self.observable_classification + return url + + def run(self): + # update logic for first time run + default_db, default_url = self.location() + if not os.path.exists(default_db) and not self.update(): + raise AnalyzerRunException( + f"Could not find or update db at {default_db} using {default_url}" + ) + + with open(default_db, "r", encoding="utf-8") as f: + logger.info(f"TweetFeeds running with {default_db}") + db = json.load(f) + for tweet in db: + if tweet["value"] == self.observable_name: + if self.filter1 and ( + self.filter1 in tweet["tags"] or self.filter1 == tweet["user"] + ): + # this checks if our user has demanded for a + # specific filter and return data based on the + # filter in default db + return tweet + elif not self.filter1: + return tweet + + if self.time == "year": + # we already have the updated data for the month + # (covers week and today options) with us; + # year is the only extended version possible + run_url = self.run_url() + logger.info(f"TweetFeeds extending using {run_url}") + + # simply make api call and search for observable + response = requests.get(run_url) + response.raise_for_status() + db = response.json() + for tweet in db: + if tweet["value"] == self.observable_name: + return tweet + + return {"found": False} + + @classmethod + def update(cls) -> bool: + """ + Update TweetFeeds database: + Our default DB gets data with + no filter for the past month + """ + + db_location, db_url = cls.location() + logger.info(f"Updating TweetFeeds {db_url} at {db_location}") + + try: + response = requests.get(db_url) + response.raise_for_status() + except requests.RequestException as e: + logger.error(f"TweetFeeds failed to update {db_url}: {e}") + return False + with open(db_location, "w", encoding="utf-8") as f: + try: + json.dump(response.json(), f) + except json.JSONDecodeError as e: + logger.error(f"TweetFeeds failed to update {db_url}: {e}") + return False + logger.info(f"TweetFeeds updated {db_url}") + return True + + @classmethod + def _monkeypatch(cls): + response = [ + { + "date": "2024-03-19 00:31:36", + "user": "Metemcyber", + "type": "url", + "value": "http://210.56.49.214", + "tags": ["#phishing"], + "tweet": "https://twitter.com/Metemcyber/status/1769884392477077774", + }, + { + "date": "2024-03-19 00:31:36", + "user": "Metemcyber", + "type": "url", + "value": "https://www.bhafulp.cn", + "tags": ["#phishing"], + "tweet": "https://twitter.com/Metemcyber/status/1769884392477077774", + }, + ] + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse( + response, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/urlhaus.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/urlhaus.py new file mode 100644 index 0000000..fa9dddc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/urlhaus.py @@ -0,0 +1,52 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class URLHaus(classes.ObservableAnalyzer): + url = "https://urlhaus-api.abuse.ch/v1/" + disable: bool = False # optional + + def update(self) -> bool: + pass + + def run(self): + if self.disable: + return {"disabled": True} + + headers = {"Accept": "application/json"} + if self.observable_classification in [ + self.ObservableTypes.DOMAIN, + self.ObservableTypes.IP, + ]: + uri = "host/" + post_data = {"host": self.observable_name} + elif self.observable_classification == self.ObservableTypes.URL: + uri = "url/" + post_data = {"url": self.observable_name} + else: + raise AnalyzerRunException( + f"not supported observable type {self.observable_classification}." + ) + + response = requests.post(self.url + uri, data=post_data, headers=headers) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/urlscan.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/urlscan.py new file mode 100644 index 0000000..acca95d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/urlscan.py @@ -0,0 +1,101 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import time + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class UrlScan(ObservableAnalyzer): + url: str = "https://urlscan.io/api/v1" + + urlscan_analysis: str + visibility: str + search_size: int + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + headers = {"Content-Type": "application/json", "User-Agent": "IntelOwl/v1.x"} + if not hasattr(self, "_api_key_name") and self.urlscan_analysis == "search": + logger.warning(f"{self.__repr__()} -> Continuing w/o API key..") + else: + headers["API-Key"] = self._api_key_name + + self.session = requests.Session() + self.session.headers = headers + if self.urlscan_analysis == "search": + result = self.__urlscan_search() + elif self.urlscan_analysis == "submit_result": + req_api_token = self.__urlscan_submit() + result = self.__poll_for_result(req_api_token) + else: + raise AnalyzerRunException( + f"not supported analysis_type {self.urlscan_analysis}." + " Supported is 'search' and 'submit_result'." + ) + return result + + def __urlscan_submit(self) -> str: + data = {"url": self.observable_name, "visibility": self.visibility} + uri = "/scan/" + response = self.session.post(self.url + uri, json=data) + # catch error description to help users to understand why it did not work + if response.status_code == 400: + error_description = response.json().get("description", "") + raise requests.HTTPError(error_description) + response.raise_for_status() + return response.json().get("api", "") + + def __poll_for_result(self, url): + # docs: "The most efficient approach would be to wait at least 10 seconds + # before starting to poll, and then only polling 2-second intervals with an + # eventual upper timeout in case the scan does not return." + max_tries = 10 + poll_distance = 2 + result = {} + time.sleep(10) + for chance in range(max_tries): + if chance: + time.sleep(poll_distance) + resp = self.session.get(url) + if resp.status_code == 404: + continue + result = resp.json() + break + return result + + def __urlscan_search(self): + params = { + "q": f'{self.observable_classification}:"{self.observable_name}"', + "size": self.search_size, + } + if self.observable_classification == self.ObservableTypes.URL: + params["q"] = "page." + params["q"] + resp = self.session.get(self.url + "/search/", params=params) + resp.raise_for_status() + result = resp.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.post", + return_value=MockUpResponse({"api": "test"}, 200), + ), + patch("requests.Session.get", return_value=MockUpResponse({}, 200)), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/validin.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/validin.py new file mode 100644 index 0000000..f281a0b --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/validin.py @@ -0,0 +1,120 @@ +import logging + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( # AnalyzerConfigurationException + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Validin(classes.ObservableAnalyzer): + """ + This analyzer is a wrapper for the Validin project. + """ + + # this is a framework implication + def update(self) -> bool: + pass + + url: str = "https://app.validin.com" + observable_classification: str + observable_name: str + scan_choice: str + _api_key_name: str + + def _run_all_queries(self, endpoints, headers): + final_response = {} + if self.observable_classification in endpoints: + for query_name, query_url in ( + endpoints.get(self.observable_classification) + ).items(): + logger.info(f"Executing query {query_name}") + try: + response = requests.get(self.url + query_url, headers=headers) + if response.status_code != 200: + logger.error(f"Query {query_name} failed") + + # we wont stop other quries from executing if one fails + final_response[f"{query_name}"] = response.json() + except requests.RequestException as e: + raise AnalyzerRunException(e) + return final_response + else: + raise AnalyzerRunException("Invalid classification") + + def _run_specific_query(self, endpoints, headers): + if self.observable_classification in endpoints: + try: + query_url = endpoints[self.observable_classification][self.scan_choice] + response = requests.get(self.url + query_url, headers=headers) + return response.json() + except KeyError: + raise AnalyzerRunException( + f"Nothing in {self.scan_choice} for" + f"{self.observable_classification}" + ) + except requests.RequestException as e: + raise AnalyzerRunException(e) + else: + raise AnalyzerRunException("Invalid classification") + + def run(self): + # code is structured in a way that endpoints + # (that are in beta stage for now) can be added easily in the future + # just add the endpoint in the dictionary with appropriate scan + # choice and classification and the code will handle it + # endpoint={classification:{scan_choice:query_url}} + endpoints = { + "domain": { + "all_records": f"/api/axon/domain/dns/history/{self.observable_name}", + "a_records": f"/api/axon/domain/dns/history/{self.observable_name}/A", + "aaaa_rec": f"/api/axon/domain/dns/history/{self.observable_name}/AAAA", + "ns_records": f"/api/axon/domain/dns/history/{self.observable_name}/NS", + "ns_for": f"/api/axon/domain/dns/history/{self.observable_name}/NS_FOR", + "ptr_records": f"/api/axon/domain/dns/hostname/{self.observable_name}", + "live_dns_query": f"/api/axon/domain/dns/live/{self.observable_name}", + }, + "ip": { + "dns_hist_rev_ip": f"/api/axon/ip/dns/history/{self.observable_name}", + "ptr_records": f"/api/axon/ip/dns/hostname/{self.observable_name}", + # here provide the ip address in the format of: 192.168.1.0/24 + "cidr_dns_history": f"/api/axon/ip/dns/history/{self.observable_name}", + "ptr_records_cidr": f"/api/axon/ip/dns/hostname/{self.observable_name}", + }, + "health": "/api/ping", + } + headers = { + "Authorization": f"BEARER {self._api_key_name}", + } + + # will run all available quries for the observable if default + if self.scan_choice == "default": + return self._run_all_queries(endpoints, headers) + else: + return self._run_specific_query(endpoints, headers) + + @classmethod + def _monkeypatch(cls): + response = { + "key": "191.121.10.0", + "effective_opts": {"type": "ip4", "limit": 100, "wildcard": False}, + "status": "finished", + "query_key": "191.121.10.0", + "records": {}, + "records_returned": 0, + "limited": False, + "error": None, + } + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse(response, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/virushee.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/virushee.py new file mode 100644 index 0000000..528bbb7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/virushee.py @@ -0,0 +1,43 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class VirusheeCheckHash(ObservableAnalyzer): + url: str = "https://api.virushee.com/file/hash/{input}" + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + self.__session = requests.Session() + if hasattr(self, "_api_key_name"): + self.__session.headers["X-API-Key"] = self._api_key_name + url = self.url.format(input=self.observable_name) + + try: + response = self.__session.get(url) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.Session.get", + return_value=MockUpResponse({"success": True}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/__init__.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_base.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_base.py new file mode 100644 index 0000000..99c2078 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_base.py @@ -0,0 +1,449 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import abc +import base64 +import logging +import time +from datetime import datetime, timedelta +from typing import Dict, Tuple + +import requests + +from api_app.analyzers_manager.classes import BaseAnalyzerMixin +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from api_app.choices import ObservableClassification + +logger = logging.getLogger(__name__) + + +class VirusTotalv3AnalyzerMixin(BaseAnalyzerMixin, metaclass=abc.ABCMeta): + url = "https://www.virustotal.com/api/v3/" + + max_tries: int + poll_distance: int + rescan_max_tries: int + rescan_poll_distance: int + include_behaviour_summary: bool + include_sigma_analyses: bool + force_active_scan_if_old: bool + days_to_say_that_a_scan_is_old: int + relationships_to_request: list + relationships_elements: int + url_sub_path: str + _api_key_name: str + + @property + def headers(self) -> dict: + return {"x-apikey": self._api_key_name} + + def _get_relationship_limit(self, relationship): + # by default, just extract the first element + limit = self.relationships_elements + # resolutions data can be more valuable and it is not lot of data + if relationship == "resolutions": + limit = 40 + return limit + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.force_active_scan = self._job.tlp == self._job.TLP.CLEAR.value + + def _vt_get_relationships( + self, + observable_name: str, + relationships_requested: list, + uri: str, + result: dict, + ): + try: + # skip relationship request if something went wrong + if "error" not in result: + relationships_in_results = result.get("data", {}).get( + "relationships", {} + ) + for relationship in self.relationships_to_request: + if relationship not in relationships_requested: + result[relationship] = { + "error": "not supported, review configuration." + } + else: + found_data = relationships_in_results.get(relationship, {}).get( + "data", [] + ) + if found_data: + logger.info( + f"found data in relationship {relationship} " + f"for observable {observable_name}." + " Requesting additional information about" + ) + rel_uri = ( + uri + f"/{relationship}" + f"?limit={self._get_relationship_limit(relationship)}" + ) + logger.debug(f"requesting uri: {rel_uri}") + response = requests.get( + self.url + rel_uri, headers=self.headers + ) + result[relationship] = response.json() + except Exception as e: + logger.error( + "something went wrong when extracting relationships" + f" for observable {observable_name}: {e}" + ) + + def _vt_get_report( + self, + obs_clfn: str, + observable_name: str, + ) -> dict: + result = {} + already_done_active_scan_because_report_was_old = False + params, uri, relationships_requested = self._get_requests_params_and_uri( + obs_clfn, observable_name + ) + for chance in range(self.max_tries): + logger.info( + f"[POLLING] (Job: {self.job_id}, observable {observable_name}) -> " + f"GET VT/v3/_vt_get_report #{chance + 1}/{self.max_tries}" + ) + + result, response = self._perform_get_request( + uri, ignore_404=True, params=params + ) + + # if it is not a file, we don't need to perform any scan + if obs_clfn != self.ObservableTypes.HASH: + break + + # this is an option to force active scan... + # .. in the case the file is not in the VT DB + # you need the binary too for this case, .. + # .. otherwise it would fail if it's not available + if response.status_code == 404: + logger.info(f"hash {observable_name} not found on VT") + if self.force_active_scan: + logger.info(f"forcing VT active scan for hash {observable_name}") + result = self._vt_scan_file(observable_name) + result["performed_active_scan"] = True + break + else: + # we should consider the chance that the very sample was already... + # ...sent and VT is already analyzing it. + # In this case, just perform a little poll for the result + attributes = result.get("data", {}).get("attributes", {}) + last_analysis_results = attributes.get("last_analysis_results", {}) + if last_analysis_results: + # at this time, if the flag if set, + # we are going to force the analysis again for old samples + if ( + self.force_active_scan_if_old + and not already_done_active_scan_because_report_was_old + ): + scan_date = attributes.get("last_analysis_date", 0) + scan_date_time = datetime.fromtimestamp(scan_date) + some_days_ago = datetime.utcnow() - timedelta( + days=self.days_to_say_that_a_scan_is_old + ) + if some_days_ago > scan_date_time: + logger.info( + f"hash {observable_name} found on VT with AV reports" + " and scan is older than" + f" {self.days_to_say_that_a_scan_is_old} days.\n" + "We will force the analysis again" + ) + # the "rescan" option will burn quotas. + # We should reduce the polling at the minimum + extracted_result = self._vt_scan_file( + observable_name, rescan_instead=True + ) + # if we were able to do a successful rescan, + # overwrite old report + if extracted_result: + result = extracted_result + already_done_active_scan_because_report_was_old = True + else: + logger.info( + f"hash {observable_name} found on VT" + f" with AV reports and scan is recent" + ) + break + else: + logger.info( + f"hash {observable_name} found on VT with AV reports" + ) + break + else: + extra_polling_times = chance + 1 + base_log = f"hash {observable_name} found on VT withOUT AV reports," + if extra_polling_times == self.max_tries: + logger.warning( + f"{base_log} reached max tries ({self.max_tries})" + ) + result["reached_max_tries_and_no_av_report"] = True + else: + logger.info(f"{base_log} performing another request...") + result["extra_polling_times"] = extra_polling_times + time.sleep(self.poll_distance) + + if already_done_active_scan_because_report_was_old: + result["performed_rescan_because_report_was_old"] = True + + if obs_clfn == self.ObservableTypes.HASH: + # Include behavioral report, if flag enabled + if self.include_behaviour_summary: + sandbox_analysis = ( + result.get("data", {}) + .get("relationships", {}) + .get("behaviours", {}) + .get("data", []) + ) + if sandbox_analysis: + logger.info( + f"found {len(sandbox_analysis)} sandbox analysis" + f" for {observable_name}," + " requesting the additional details" + ) + result["behaviour_summary"] = self._fetch_behaviour_summary( + observable_name + ) + + # Include sigma analysis report, if flag enabled + if self.include_sigma_analyses: + sigma_analysis = ( + result.get("data", {}) + .get("relationships", {}) + .get("sigma_analysis", {}) + .get("data", []) + ) + if sigma_analysis: + logger.info( + f"found {len(sigma_analysis)} sigma analysis" + f" for {observable_name}," + " requesting the additional details" + ) + result["sigma_analyses"] = self._fetch_sigma_analyses( + observable_name + ) + + if self.relationships_to_request: + self._vt_get_relationships( + observable_name, relationships_requested, uri, result + ) + uri_prefix, uri_postfix = self._get_url_prefix_postfix(result) + result["link"] = f"https://www.virustotal.com/gui/{uri_prefix}/{uri_postfix}" + + return result + + def _get_url_prefix_postfix(self, result: Dict) -> Tuple[str, str]: + uri_postfix = self._job.observable_name + if self._job.observable_classification == ObservableClassification.DOMAIN.value: + uri_prefix = "domain" + elif self._job.observable_classification == ObservableClassification.IP.value: + uri_prefix = "ip-address" + elif self._job.observable_classification == ObservableClassification.URL.value: + uri_prefix = "url" + uri_postfix = result.get("data", {}).get("id", self._job.sha256) + else: # hash + uri_prefix = "search" + return uri_prefix, uri_postfix + + def _vt_scan_file(self, md5: str, rescan_instead: bool = False) -> dict: + if rescan_instead: + logger.info(f"(Job: {self.job_id}, {md5}) -> VT analyzer requested rescan") + files = {} + uri = f"files/{md5}/analyse" + poll_distance = self.rescan_poll_distance + max_tries = self.rescan_max_tries + else: + logger.info(f"(Job: {self.job_id}, {md5}) -> VT analyzer requested scan") + try: + binary = self._job.file.read() + except Exception: + raise AnalyzerRunException( + "IntelOwl error: couldn't retrieve the binary" + f" to perform a scan (Job: {self.job_id}, {md5})" + ) + files = {"file": binary} + uri = "files" + poll_distance = self.poll_distance + max_tries = self.max_tries + + result, _ = self._perform_post_request(uri, files=files) + + result_data = result.get("data", {}) + scan_id = result_data.get("id", "") + if not scan_id: + raise AnalyzerRunException( + "no scan_id given by VirusTotal to retrieve the results" + f" (Job: {self.job_id}, {md5})" + ) + # max 5 minutes waiting + got_result = False + uri = f"analyses/{scan_id}" + logger.info( + "Starting POLLING for Scan results. " + f"Poll Distance {poll_distance}, tries {max_tries}, ScanID {scan_id}" + f" (Job: {self.job_id}, {md5})" + ) + for chance in range(max_tries): + time.sleep(poll_distance) + result, _ = self._perform_get_request(uri, files=files) + analysis_status = ( + result.get("data", {}).get("attributes", {}).get("status", "") + ) + logger.info( + f"[POLLING] (Job: {self.job_id}, {md5}) -> " + f"GET VT/v3/_vt_scan_file #{chance + 1}/{self.max_tries} " + f"status:{analysis_status}" + ) + if analysis_status == "completed": + got_result = True + break + + result = {} + if got_result: + # retrieve the FULL report, not only scans results. + # If it's a new sample, it's free of charge. + result = self._vt_get_report(self.ObservableTypes.HASH, md5) + else: + message = ( + f"[POLLING] (Job: {self.job_id}, {md5}) -> " + f"max polls tried, no result" + ) + # if we tried a rescan, we can still use the old report + if rescan_instead: + logger.info(message) + else: + raise AnalyzerRunException(message) + + return result + + def _perform_get_request(self, uri: str, ignore_404=False, **kwargs): + return self._perform_request(uri, method="GET", ignore_404=ignore_404, **kwargs) + + def _perform_post_request(self, uri: str, ignore_404=False, **kwargs): + return self._perform_request( + uri, method="POST", ignore_404=ignore_404, **kwargs + ) + + def _perform_request(self, uri: str, method: str, ignore_404=False, **kwargs): + error = None + try: + url = self.url + uri + if method == "GET": + response = requests.get(url, headers=self.headers, **kwargs) + elif method == "POST": + response = requests.post(url, headers=self.headers, **kwargs) + else: + raise NotImplementedError() + logger.info(f"requests done to: {response.request.url} ") + logger.debug(f"text: {response.text}") + result = response.json() + # https://developers.virustotal.com/reference/errors + error = result.get("error", {}) + # this case is not a real error,... + # .. it happens when a requested object is not found and that's normal + if not ignore_404 or not response.status_code == 404: + response.raise_for_status() + except Exception as e: + error_message = f"Raised Error: {e}. Error data: {error}" + raise AnalyzerRunException(error_message) + return result, response + + def _fetch_behaviour_summary(self, observable_name: str) -> dict: + endpoint = f"files/{observable_name}/behaviour_summary" + result, _ = self._perform_get_request(endpoint, ignore_404=True) + return result + + def _fetch_sigma_analyses(self, observable_name: str) -> dict: + endpoint = f"sigma_analyses/{observable_name}" + result, _ = self._perform_get_request(endpoint, ignore_404=True) + return result + + @classmethod + def _get_relationship_for_classification(cls, obs_clfn: str): + # reference: https://developers.virustotal.com/reference/metadata + if obs_clfn == cls.ObservableTypes.DOMAIN: + relationships = [ + "communicating_files", + "historical_whois", + "referrer_files", + "resolutions", + "siblings", + "subdomains", + "collections", + "historical_ssl_certificates", + ] + elif obs_clfn == cls.ObservableTypes.IP: + relationships = [ + "communicating_files", + "historical_whois", + "referrer_files", + "resolutions", + "collections", + "historical_ssl_certificates", + ] + elif obs_clfn == cls.ObservableTypes.URL: + relationships = [ + "last_serving_ip_address", + "collections", + "network_location", + ] + elif obs_clfn == cls.ObservableTypes.HASH: + relationships = [ + # behaviors is necessary to check if there are sandbox analysis + "behaviours", + "bundled_files", + "comments", + "contacted_domains", + "contacted_ips", + "contacted_urls", + "execution_parents", + "pe_resource_parents", + "votes", + "distributors", + "pe_resource_children", + "dropped_files", + "collections", + ] + else: + raise AnalyzerRunException( + f"Not supported observable type {obs_clfn}. " + "Supported are: hash, ip, domain and url." + ) + return relationships + + def _get_requests_params_and_uri(self, obs_clfn: str, observable_name: str): + params = {} + # in this way, you just retrieved metadata about relationships + # if you like to get all the data about specific relationships,... + # ..you should perform another query + # check vt3 API docs for further info + relationships_requested = self._get_relationship_for_classification(obs_clfn) + if obs_clfn == self.ObservableTypes.DOMAIN: + uri = f"domains/{observable_name}" + elif obs_clfn == self.ObservableTypes.IP: + uri = f"ip_addresses/{observable_name}" + elif obs_clfn == self.ObservableTypes.URL: + url_id = ( + base64.urlsafe_b64encode(observable_name.encode()).decode().strip("=") + ) + uri = f"urls/{url_id}" + elif obs_clfn == self.ObservableTypes.HASH: + uri = f"files/{observable_name}" + else: + raise AnalyzerRunException( + f"Not supported observable type {obs_clfn}. " + "Supported are: hash, ip, domain and url." + ) + + if relationships_requested: + # this won't cost additional quota + # it just helps to understand if there is something to look for there + # so, if there is, we can make API requests without wasting quotas + params["relationships"] = ",".join(relationships_requested) + if self.url_sub_path: + if not self.url_sub_path.startswith("/"): + uri += "/" + uri += self.url_sub_path + return params, uri, relationships_requested diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_get.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_get.py new file mode 100644 index 0000000..76d70d8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_get.py @@ -0,0 +1,66 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from .vt3_base import VirusTotalv3AnalyzerMixin + + +class VirusTotalv3(ObservableAnalyzer, VirusTotalv3AnalyzerMixin): + def run(self): + result = self._vt_get_report( + self.observable_classification, + self.observable_name, + ) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + side_effect=[ + # for _vt_get_report + MockUpResponse( + { + "data": { + "attributes": { + "status": "completed", + "last_analysis_results": {"test": "test"}, + # must be earlier than 30 days ago + "last_analysis_date": 1590000000, + } + } + }, + 200, + ), + # for _vt_scan_file + MockUpResponse( + { + "data": { + "attributes": { + "status": "completed", + } + } + }, + 200, + ), + # for /behaviour_summary + MockUpResponse({}, 200), + # for /sigma_analyses + MockUpResponse({}, 200), + ], + ), + patch( + "requests.post", + # for _vt_scan_file + return_value=MockUpResponse( + {"scan_id": "scan_id_test", "data": {"id": "id_test"}}, 200 + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_intelligence_search.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_intelligence_search.py new file mode 100644 index 0000000..939acfd --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vt/vt3_intelligence_search.py @@ -0,0 +1,54 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Dict + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +from ...exceptions import AnalyzerRunException +from .vt3_base import VirusTotalv3AnalyzerMixin + + +class VirusTotalv3Intelligence(ObservableAnalyzer, VirusTotalv3AnalyzerMixin): + url = "https://www.virustotal.com/api/v3/intelligence" + + limit: int + order_by: str + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + # this is a limit forced by VT service + if self.limit > 300: + self.limit = 300 + + def run(self): + # ref: https://developers.virustotal.com/reference/intelligence-search + params = { + "query": self.observable_name, + "limit": self.limit, + } + if self.order_by: + params["order"] = self.order_by + try: + response = requests.get( + self.url + "/search", params=params, headers=self.headers + ) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vulners.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vulners.py new file mode 100644 index 0000000..2efc53f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/vulners.py @@ -0,0 +1,65 @@ +import logging + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + +logger = logging.getLogger(__name__) + + +class Vulners(classes.ObservableAnalyzer): + """ + This analyzer is a wrapper for the vulners project. + """ + + score_AI: bool = False + skip: int = 0 + size: int = 5 + _api_key_name: str + url = "https://vulners.com/api/v3" + + def search_ai(self): + return requests.post( + url=self.url + "/ai/scoretext/", + headers={"Content-Type": "application/json"}, + json={"text": self.observable_name, "apiKey": self._api_key_name}, + ) + + def search_databse(self): + return requests.post( + url=self.url + "/search/lucene", + headers={"Content-Type": "application/json"}, + json={ + "query": self.observable_name, + "skip": self.size, + "size": self.skip, + "apiKey": self._api_key_name, + }, + ) + + def run(self): + response = None + if self.score_AI: + response = self.search_ai() + else: + response = self.search_databse() + response.raise_for_status() + return response.json() + + # this is a framework implication + def update(self) -> bool: + pass + + @classmethod + def _monkeypatch(cls): + response = {"result": "OK", "data": {"score": [6.5, "NONE"]}} + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse(response, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/whoisripe.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/whoisripe.py new file mode 100644 index 0000000..bd1e7d3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/whoisripe.py @@ -0,0 +1,31 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class WhoIsRipeAPI(classes.ObservableAnalyzer): + url: str = "https://rest.db.ripe.net/search.json" + + def run(self): + params = {"query-string": self.observable_name} + + response = requests.get(self.url, params=params) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/whoisxmlapi.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/whoisxmlapi.py new file mode 100644 index 0000000..4dfce7d --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/whoisxmlapi.py @@ -0,0 +1,36 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class Whoisxmlapi(classes.ObservableAnalyzer): + url: str = "https://www.whoisxmlapi.com/whoisserver/WhoisService" + + _api_key_name: str + + def run(self): + params = { + "apiKey": self._api_key_name, + "domainName": self.observable_name, + "outputFormat": "JSON", + } + response = requests.get(self.url, params=params) + response.raise_for_status() + + return response.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/wigle.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/wigle.py new file mode 100644 index 0000000..8e635f2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/wigle.py @@ -0,0 +1,73 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerConfigurationException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class WiGLE(classes.ObservableAnalyzer): + url: str = "https://api.wigle.net" + + _api_key_name: str + search_type: str + + def __prepare_args(self): + # Sample Argument: operator=001;type=GSM + args = self.observable_name.split(";") + self.args = {} + for arg in args: + try: + key, value = arg.split("=") + except ValueError: + key = "wifiNetworkId" + value = arg + self.args[key] = value + + def run(self): + self.__prepare_args() + + if self.search_type == "WiFi Network": + uri = f"/api/v3/detail/wifi/{self.args.get('wifiNetworkId')}" + elif self.search_type == "CDMA Network": + uri = ( + f"/api/v3/detail/cell/CDMA/{self.args.get('sid')}/" + f"{self.args.get('nid')}/{self.args.get('bsid')}" + ) + elif self.search_type == "Bluetooth Network": + uri = f"/api/v3/detail/bt/{self.args.get('btNetworkId')}" + elif self.search_type == "GSM/LTE/WCDMA Network": + uri = ( + "/api/v3/detail/cell/" + f"{self.args.get('type')}/{self.args.get('operator')}/" + f"{self.args.get('lac')}/{self.args.get('cid')}" + ) + else: + raise AnalyzerConfigurationException( + f"search type: '{self.search_type}' not supported." + "Supported are: 'WiFi Network', 'CDMA Network', " + "'Bluetooth Network', 'GSM/LTE/WCDMA Network'" + ) + + response = requests.get( + self.url + uri, + headers={"Authorization": "Basic " + self._api_key_name}, + ) + response.raise_for_status() + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/xforce.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/xforce.py new file mode 100644 index 0000000..e351086 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/xforce.py @@ -0,0 +1,93 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from urllib.parse import quote_plus + +import requests +from requests.auth import HTTPBasicAuth + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import AnalyzerRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class XForce(classes.ObservableAnalyzer): + url: str = "https://exchange.xforce.ibmcloud.com/api" + web_url: str = "https://exchange.xforce.ibmcloud.com" + + _api_key_name: str + _api_password_name: str + malware_only: bool + timeout: int = 5 + + @classmethod + def update(cls) -> bool: + pass + + def run(self): + auth = HTTPBasicAuth(self._api_key_name, self._api_password_name) + headers = {"Accept": "application/json"} + + endpoints = self._get_endpoints() + result = {} + for endpoint in endpoints: + if self.observable_classification == self.ObservableTypes.URL: + observable_to_check = quote_plus(self.observable_name) + else: + observable_to_check = self.observable_name + url = f"{self.url}/{endpoint}/{observable_to_check}" + response = requests.get( + url, auth=auth, headers=headers, timeout=self.timeout + ) + if response.status_code == 404: + result["found"] = False + else: + response.raise_for_status() + result[endpoint] = response.json() + path = self.observable_classification + if self.observable_classification == self.ObservableTypes.DOMAIN: + path = self.ObservableTypes.URL + elif self.observable_classification == self.ObservableTypes.HASH: + path = "malware" + result[endpoint]["link"] = f"{self.web_url}/{path}/{observable_to_check}" + + return result + + def _get_endpoints(self): + """Return API endpoints for observable type + + :return: API endpoints + :rtype: list + """ + endpoints = [] + if self.observable_classification == self.ObservableTypes.IP: + if not self.malware_only: + endpoints.extend(["ipr", "ipr/history"]) + endpoints.append("ipr/malware") + elif self.observable_classification == self.ObservableTypes.HASH: + endpoints.append("malware") + elif self.observable_classification in [ + self.ObservableTypes.URL, + self.ObservableTypes.DOMAIN, + ]: + if not self.malware_only: + endpoints.extend(["url", "url/history"]) + endpoints.append("url/malware") + else: + raise AnalyzerRunException( + f"{self.observable_classification} not supported" + ) + + return endpoints + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/yaraify.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/yaraify.py new file mode 100644 index 0000000..7a0f0bb --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/yaraify.py @@ -0,0 +1,42 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager.classes import ObservableAnalyzer +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class YARAify(ObservableAnalyzer): + url: str = "https://yaraify-api.abuse.ch/api/v1/" + + query: str + result_max: int + _api_key_name: str + + def run(self): + data = {"search_term": self.observable_name, "query": self.query} + + if self.observable_classification == self.ObservableTypes.GENERIC: + data["result_max"] = self.result_max + + if getattr(self, "_api_key_name", None): + data["malpedia-token"] = self._api_key_name + + response = requests.post(self.url, json=data) + response.raise_for_status() + + result = response.json() + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/yeti.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/yeti.py new file mode 100644 index 0000000..7f213ac --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/yeti.py @@ -0,0 +1,49 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class YETI(classes.ObservableAnalyzer): + verify_ssl: bool + results_count: int + regex: False + _url_key_name: str + _api_key_name: str + + def run(self): + # request payload + payload = { + "filter": {"value": self._job.observable_name}, + "params": {"regex": self.regex, "range": self.results_count}, + } + headers = {"Accept": "application/json", "X-Api-Key": self._api_key_name} + if self._url_key_name and self._url_key_name.endswith("/"): + self._url_key_name = self._url_key_name[:-1] + url = f"{self._url_key_name}/api/v2/observablesearch/" + + # search for observables + resp = requests.post( + url=url, + headers=headers, + json=payload, + verify=self.verify_ssl, + ) + resp.raise_for_status() + + return resp.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse([], 200), + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/zoomeye.py b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/zoomeye.py new file mode 100644 index 0000000..bc3d0fe --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/observable_analyzers/zoomeye.py @@ -0,0 +1,90 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests + +from api_app.analyzers_manager import classes +from api_app.analyzers_manager.exceptions import ( + AnalyzerConfigurationException, + AnalyzerRunException, +) +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class ZoomEye(classes.ObservableAnalyzer): + url: str = "https://api.zoomeye.org/" + + search_type: str + query: str + page: int + facets: str + history: bool + _api_key_name: str + + @classmethod + def update(cls) -> bool: + pass + + def __build_zoomeye_url(self): + if self.observable_classification == self.ObservableTypes.IP: + self.query += f" ip:{self.observable_name}" + else: + self.query += f" hostname:{self.observable_name}" + self.search_type = "host" + + if self.search_type in ["host", "web"]: + self.final_url = self.url + self.search_type + "/search?query=" + self.final_url += self.query + + if self.page: + self.final_url += f"&page={self.page}" + + if self.facets: + self.final_url += f"&facet={','.join(self.facets)}" + + elif self.search_type == "both": + self.final_url = self.url + "both/search?" + if self.history: + self.final_url += f"history={self.history}&" + self.final_url += f"ip={self.observable_name}" + else: + raise AnalyzerConfigurationException( + f"search type: '{self.search_type}' not supported." + "Supported are: 'host', 'web', 'both'" + ) + + def run(self): + self.__build_zoomeye_url() + + try: + response = requests.get( + self.final_url, headers={"API-KEY": self._api_key_name} + ) + response.raise_for_status() + except requests.RequestException as e: + raise AnalyzerRunException(e) + + result = {"custom_options": {}} + result["custom_options"]["search_type"] = self.search_type + result["custom_options"]["query"] = self.query + if self.page: + result["custom_options"]["page"] = self.page + if self.facets: + result["custom_options"]["facet"] = self.facets + if self.history and self.search_type == "both": + result["custom_options"]["history"] = self.history + result.update(response.json()) + + return result + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.get", + return_value=MockUpResponse({}, 200), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/queryset.py b/Submodules/IntelOwl/api_app/analyzers_manager/queryset.py new file mode 100644 index 0000000..ce6da8f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/queryset.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING, Type + +from api_app.queryset import AbstractReportQuerySet + +if TYPE_CHECKING: + from api_app.analyzers_manager.serializers import AnalyzerReportBISerializer + + +class AnalyzerReportQuerySet(AbstractReportQuerySet): + @classmethod + def _get_bi_serializer_class(cls) -> Type["AnalyzerReportBISerializer"]: + from api_app.analyzers_manager.serializers import AnalyzerReportBISerializer + + return AnalyzerReportBISerializer diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/repo_downloader.sh b/Submodules/IntelOwl/api_app/analyzers_manager/repo_downloader.sh new file mode 100644 index 0000000..23bd8ae --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/repo_downloader.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# this is a simple script that downloads public yara repositories and make some changes on their configuration +# we have also added the download of other tools like quark-engine rules, dnstwist dictionaries and exiftool + +# I suggest you to modify this script based on your needs. +# Example: you may want to add a new repository. Add the clone here +# Example: you may want to remove some of the rules available in the downloaded repositories. Remove them here. + + +# This script can be disabled during development using REPO_DOWNLOADER_ENABLED=true env variable +if [ "$REPO_DOWNLOADER_ENABLED" = "false" ]; then echo "Skipping repo_downloader.sh in DEVELOPMENT mode"; exit 0; fi + +# Download rules for quark-engine analyzer +cd ~ +freshquark +# this is the default directory used by Quark-Engine +chown -R www-data:www-data ~/.quark-engine + +# Clone dictionaries for dnstwist analyzer +cd /opt/deploy +svn export https://github.com/elceef/dnstwist/tags/20230402/dictionaries dnstwist-dictionaries + +# download exiftool +# https://exiftool.org/install.html#Unix +mkdir exiftool_download +cd exiftool_download +version=$(curl https://exiftool.org/ver.txt) +echo "$version" >> exiftool_version.txt +wget "https://exiftool.org/Image-ExifTool-$version.tar.gz" +gzip -dc "Image-ExifTool-$version.tar.gz" | tar -xf - +cd "Image-ExifTool-$version" +chown -R www-data:www-data /opt/deploy/exiftool_download + + diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/serializers.py b/Submodules/IntelOwl/api_app/analyzers_manager/serializers.py new file mode 100644 index 0000000..4847c63 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/serializers.py @@ -0,0 +1,35 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from ..serializers.plugin import ( + PythonConfigSerializer, + PythonConfigSerializerForMigration, +) +from ..serializers.report import AbstractReportBISerializer, AbstractReportSerializer +from .models import AnalyzerConfig, AnalyzerReport + + +class AnalyzerReportSerializer(AbstractReportSerializer): + class Meta: + model = AnalyzerReport + fields = AbstractReportSerializer.Meta.fields + list_serializer_class = AbstractReportSerializer.Meta.list_serializer_class + + +class AnalyzerReportBISerializer(AbstractReportBISerializer): + class Meta: + model = AnalyzerReport + fields = AbstractReportBISerializer.Meta.fields + list_serializer_class = AbstractReportBISerializer.Meta.list_serializer_class + + +class AnalyzerConfigSerializer(PythonConfigSerializer): + class Meta: + model = AnalyzerConfig + exclude = PythonConfigSerializer.Meta.exclude + list_serializer_class = PythonConfigSerializer.Meta.list_serializer_class + + +class AnalyzerConfigSerializerForMigration(PythonConfigSerializerForMigration): + class Meta: + model = AnalyzerConfig + exclude = PythonConfigSerializerForMigration.Meta.exclude diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/signals.py b/Submodules/IntelOwl/api_app/analyzers_manager/signals.py new file mode 100644 index 0000000..e574bdc --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/signals.py @@ -0,0 +1,31 @@ +import logging +import uuid + +from django.conf import settings +from django.dispatch import receiver + +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.signals import migrate_finished +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +@receiver(migrate_finished) +def post_migrate_analyzers_manager( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from intel_owl.tasks import refresh_cache + + refresh_cache.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=3, + args=[AnalyzerConfig.python_path], + ) diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/ssh_gitpython.sh b/Submodules/IntelOwl/api_app/analyzers_manager/ssh_gitpython.sh new file mode 100644 index 0000000..1fc308f --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/ssh_gitpython.sh @@ -0,0 +1,2 @@ +#!/bin/bash +ssh -i "/opt/deploy/files_required/my_gitpython_key" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$@" \ No newline at end of file diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/urls.py b/Submodules/IntelOwl/api_app/analyzers_manager/urls.py new file mode 100644 index 0000000..eda6b58 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/urls.py @@ -0,0 +1,22 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +from .views import AnalyzerActionViewSet, AnalyzerConfigViewSet + +# Routers provide an easy way of automatically determining the URL conf. +router = routers.DefaultRouter(trailing_slash=False) +router.register( + r"jobs/(?P\d+)/analyzer/(?P\w+)", + AnalyzerActionViewSet, + basename="analyzerreport", +) +router.register(r"analyzer", AnalyzerConfigViewSet, basename="analyzer") + + +urlpatterns = [ + # Viewsets + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/analyzers_manager/views.py b/Submodules/IntelOwl/api_app/analyzers_manager/views.py new file mode 100644 index 0000000..1c27a32 --- /dev/null +++ b/Submodules/IntelOwl/api_app/analyzers_manager/views.py @@ -0,0 +1,28 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging + +from ..views import PythonConfigViewSet, PythonReportActionViewSet +from .filters import AnalyzerConfigFilter +from .models import AnalyzerReport +from .serializers import AnalyzerConfigSerializer + +logger = logging.getLogger(__name__) + + +__all__ = [ + "AnalyzerConfigViewSet", + "AnalyzerActionViewSet", +] + + +class AnalyzerConfigViewSet(PythonConfigViewSet): + serializer_class = AnalyzerConfigSerializer + filterset_class = AnalyzerConfigFilter + + +class AnalyzerActionViewSet(PythonReportActionViewSet): + @classmethod + @property + def report_model(cls): + return AnalyzerReport diff --git a/Submodules/IntelOwl/api_app/apps.py b/Submodules/IntelOwl/api_app/apps.py new file mode 100644 index 0000000..e976ff0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/apps.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from logging import getLogger + +from django.apps import AppConfig + +logger = getLogger(__name__) + + +class ApiAppConfig(AppConfig): + name = "api_app" + + def ready(self): # skipcq: PYL-R0201 + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/choices.py b/Submodules/IntelOwl/api_app/choices.py new file mode 100644 index 0000000..dee3bd9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/choices.py @@ -0,0 +1,134 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import enum +import typing +from pathlib import PosixPath + +import _operator +from django.db import models + + +class PythonModuleBasePaths(models.TextChoices): + ObservableAnalyzer = ( + PosixPath("api_app.analyzers_manager.observable_analyzers"), + "Observable Analyzer", + ) + FileAnalyzer = ( + PosixPath("api_app.analyzers_manager.file_analyzers"), + "File Analyzer", + ) + Connector = PosixPath("api_app.connectors_manager.connectors"), "Connector" + Ingestor = PosixPath("api_app.ingestors_manager.ingestors"), "Ingestor" + Visualizer = PosixPath("api_app.visualizers_manager.visualizers"), "Visualizer" + Pivot = PosixPath("api_app.pivots_manager.pivots"), "Pivot" + + +class TLP(models.TextChoices): + CLEAR = "CLEAR" + GREEN = "GREEN" + AMBER = "AMBER" + RED = "RED" + + @classmethod + def get_priority(cls, tlp): + order = { + cls.CLEAR: 0, + cls.GREEN: 1, + cls.AMBER: 2, + cls.RED: 3, + } + return order[tlp] + + def __compare(self, other, operator): + if not isinstance(other, TLP): + raise TypeError(f"Can sum {self.__class__.__name__} with {type(other)}") + + return operator(self.get_priority(self), self.get_priority(other)) + + def __gt__(self, other): + return self.__compare(other, _operator.gt) + + def __lt__(self, other): + return self.__compare(other, _operator.lt) + + +class Status(models.TextChoices): + PENDING = "pending", "pending" + RUNNING = "running", "running" + + ANALYZERS_RUNNING = "analyzers_running", "analyzers_running" + ANALYZERS_COMPLETED = "analyzers_completed", "analyzers_completed" + + CONNECTORS_RUNNING = "connectors_running", "connectors_running" + CONNECTORS_COMPLETED = "connectors_completed", "connectors_completed" + + PIVOTS_RUNNING = "pivots_running", "pivots_running" + PIVOTS_COMPLETED = "pivots_completed", "pivots_completed" + + VISUALIZERS_RUNNING = "visualizers_running", "visualizers_running" + VISUALIZERS_COMPLETED = "visualizers_completed", "visualizers_completed" + + REPORTED_WITHOUT_FAILS = "reported_without_fails", "reported_without_fails" + REPORTED_WITH_FAILS = "reported_with_fails", "reported_with_fails" + KILLED = "killed", "killed" + FAILED = "failed", "failed" + + @classmethod + def get_enums_with_suffix( + cls, suffix: str + ) -> typing.Generator[enum.Enum, None, None]: + for key in cls: + if key.name.endswith(suffix): + yield key + + @classmethod + def running_statuses(cls) -> typing.List["Status"]: + return list(cls.get_enums_with_suffix("_RUNNING")) + + @classmethod + def partial_statuses(cls) -> typing.List["Status"]: + return list(cls.get_enums_with_suffix("_COMPLETED")) + + @classmethod + def final_statuses(cls) -> typing.List["Status"]: + return [ + cls.REPORTED_WITHOUT_FAILS, + cls.REPORTED_WITH_FAILS, + cls.KILLED, + cls.FAILED, + ] + + +class ObservableClassification(models.TextChoices): + IP = "ip" + URL = "url" + DOMAIN = "domain" + HASH = "hash" + GENERIC = "generic" + EMPTY = "" + + +class ScanMode(models.IntegerChoices): + FORCE_NEW_ANALYSIS = 1 + CHECK_PREVIOUS_ANALYSIS = 2 + + +class ReportStatus(models.TextChoices): + FAILED = "FAILED" + PENDING = "PENDING" + RUNNING = "RUNNING" + SUCCESS = "SUCCESS" + KILLED = "KILLED" + + @classmethod + def final_statuses(cls): + return [cls.FAILED, cls.SUCCESS, cls.KILLED] + + +class ParamTypes(models.TextChoices): + INT = "int" + FLOAT = "float" + STR = "str" + BOOL = "bool" + LIST = "list" + DICT = "dict" diff --git a/Submodules/IntelOwl/api_app/classes.py b/Submodules/IntelOwl/api_app/classes.py new file mode 100644 index 0000000..29d970f --- /dev/null +++ b/Submodules/IntelOwl/api_app/classes.py @@ -0,0 +1,314 @@ +import logging +import traceback +import typing +from abc import ABCMeta, abstractmethod +from pathlib import PosixPath + +import requests +from billiard.exceptions import SoftTimeLimitExceeded +from django.conf import settings +from django.utils import timezone +from django.utils.functional import cached_property +from requests import HTTPError + +from api_app.models import AbstractReport, Job, PythonConfig, PythonModule +from certego_saas.apps.user.models import User + +logger = logging.getLogger(__name__) + + +class Plugin(metaclass=ABCMeta): + """ + Abstract Base class for plugins. + For internal use only. + """ + + def __init__( + self, + config: PythonConfig, + **kwargs, + ): + self._config = config + self.kwargs = kwargs + # some post init processing + + # monkeypatch if in test suite + if settings.STAGE_CI or settings.MOCK_CONNECTIONS: + self._monkeypatch() + + @property + def name(self): + return self._config.name + + @classmethod + @property + @abstractmethod + def python_base_path(cls) -> PosixPath: + NotImplementedError() + + @classmethod + def all_subclasses(cls): + posix_dir = PosixPath(str(cls.python_base_path).replace(".", "/")) + for plugin in posix_dir.rglob("*.py"): + if plugin.stem == "__init__": + continue + + package = f"{str(plugin.parent).replace('/', '.')}.{plugin.stem}" + __import__(package) + classes = cls.__subclasses__() + return sorted( + [class_ for class_ in classes if not class_.__name__.startswith("MockUp")], + key=lambda x: x.__name__, + ) + + @cached_property + def _job(self) -> "Job": + return Job.objects.get(pk=self.job_id) + + @property + def job_id(self) -> int: + return self._job_id + + @job_id.setter + def job_id(self, value): + self._job_id = value + + @cached_property + def _user(self): + return self._job.user + + def __repr__(self): + return str(self) + + def __str__(self): + try: + return f"({self.__class__.__name__}, job: #{self.job_id})" + except AttributeError: + return f"{self.__class__.__name__}" + + def config(self, runtime_configuration: typing.Dict): + self.__parameters = self._config.read_configured_params( + self._user, runtime_configuration + ) + for parameter in self.__parameters: + attribute_name = ( + f"_{parameter.name}" if parameter.is_secret else parameter.name + ) + setattr(self, attribute_name, parameter.value) + logger.debug( + f"Adding to {self.__class__.__name__} " + f"param {attribute_name} with value {parameter.value} " + ) + + def before_run(self): + """ + function called directly before run function. + """ + + @abstractmethod + def run(self) -> dict: + """ + Called from *start* fn and wrapped in a try-catch block. + Should be overwritten in child class + :returns report + """ + + def after_run(self): + """ + function called after run function. + """ + self.report.end_time = timezone.now() + self.report.save() + + def after_run_success(self, content: typing.Any): + if isinstance(content, typing.Generator): + content = list(content) + self.report.report = content + self.report.status = self.report.Status.SUCCESS.value + self.report.save(update_fields=["status", "report"]) + + def log_error(self, e): + if isinstance( + e, (*self.get_exceptions_to_catch(), SoftTimeLimitExceeded, HTTPError) + ): + error_message = self.get_error_message(e) + logger.error(error_message) + else: + traceback.print_exc() + error_message = self.get_error_message(e, is_base_err=True) + logger.exception(error_message) + + def after_run_failed(self, e: Exception): + self.report.errors.append(str(e)) + self.report.status = self.report.Status.FAILED + self.report.save(update_fields=["status", "errors"]) + if isinstance(e, HTTPError) and ( + hasattr(e, "response") + and hasattr(e.response, "status_code") + and e.response.status_code == 429 + ): + self.disable_for_rate_limit() + else: + self.log_error(e) + if settings.STAGE_CI: + raise e + + @classmethod + @property + @abstractmethod + def report_model(cls) -> typing.Type[AbstractReport]: + """ + Returns Model to be used for *init_report_object* + """ + raise NotImplementedError() + + @classmethod + @property + @abstractmethod + def config_model(cls) -> typing.Type[PythonConfig]: + """ + Returns Model to be used for *init_report_object* + """ + raise NotImplementedError() + + @abstractmethod + def get_exceptions_to_catch(self) -> list: + """ + Returns list of `Exception`'s to handle. + """ + raise NotImplementedError() + + def get_error_message(self, err, is_base_err=False): + """ + Returns error message for + *_handle_analyzer_exception* fn + """ + return ( + f"{self}." + f" {'Unexpected error' if is_base_err else f'{self.config_model.__name__} error'}:" # noqa + f" '{err}'" + ) + + def start( + self, job_id: int, runtime_configuration: dict, task_id: str, *args, **kwargs + ): + """ + Entrypoint function to execute the plugin. + calls `before_run`, `run`, `after_run` + in that order with exception handling. + """ + self.job_id = job_id + self.report: AbstractReport = self._config.generate_empty_report( + self._job, task_id, AbstractReport.Status.RUNNING.value + ) + try: + self.config(runtime_configuration) + self.before_run() + _result = self.run() + except Exception as e: + self.after_run_failed(e) + else: + self.after_run_success(_result) + finally: + # add end time of process + self.after_run() + + def _handle_exception(self, exc, is_base_err: bool = False) -> None: + if not is_base_err: + traceback.print_exc() + error_message = self.get_error_message(exc, is_base_err=is_base_err) + logger.error(error_message) + self.report.errors.append(str(exc)) + self.report.status = self.report.Status.FAILED + + @classmethod + def _monkeypatch(cls, patches: list = None) -> None: + """ + Hook to monkey-patch class for testing purposes. + """ + if patches is None: + patches = [] + for mock_fn in patches: + cls.start = mock_fn(cls.start) + + @classmethod + @property + def python_module(cls) -> PythonModule: + valid_module = cls.__module__.replace(str(cls.python_base_path), "") + # remove the starting dot + valid_module = valid_module[1:] + return PythonModule.objects.get( + module=f"{valid_module}.{cls.__name__}", base_path=cls.python_base_path + ) + + @classmethod + def update(cls) -> bool: + raise NotImplementedError("No update implemented") + + def _get_health_check_url(self, user: User = None) -> typing.Optional[str]: + params = ( + self._config.parameters.annotate_configured(self._config, user) + .annotate_value_for_user(self._config, user) + .filter(name__icontains="url") + ) + for param in params: + if not param.configured or not param.value: + continue + url = param.value + logger.info(f"Url retrieved to verify is {param.name} for {self}") + return url + if hasattr(self, "url") and self.url: + return self.url + return None + + def health_check(self, user: User = None) -> bool: + url = self._get_health_check_url(user) + if url and url.startswith("http"): + if settings.STAGE_CI or settings.MOCK_CONNECTIONS: + return True + logger.info(f"healthcheck url {url} for {self}") + try: + # momentarily set this to False to + # avoid fails for https services + response = requests.head(url, timeout=10, verify=False) + response.raise_for_status() + except ( + requests.exceptions.ConnectionError, + requests.exceptions.Timeout, + requests.exceptions.HTTPError, + ) as e: + logger.info(f"healthcheck failed: url {url}" f" for {self}. Error: {e}") + return False + else: + return True + raise NotImplementedError() + + def disable_for_rate_limit(self): + logger.info(f"Trying to disable for rate limit {self}") + if self._user.has_membership(): + org_configuration = self._config.get_or_create_org_configuration( + self._user.membership.organization + ) + if org_configuration.rate_limit_timeout is not None: + api_key_parameter = self.__parameters.filter( + name__contains="api_key" + ).first() + # if we do not have api keys OR the api key was org based + # OR if the api key is not actually required and we do not have it set + if ( + not api_key_parameter + or api_key_parameter.is_from_org + or (not api_key_parameter.required and not api_key_parameter.value) + ): + org_configuration.disable_for_rate_limit() + else: + logger.warning( + f"Not disabling {self} because api key used is personal" + ) + else: + logger.warning( + f"You are trying to disable {self}" + " for rate limit without specifying a timeout." + ) + else: + logger.info(f"User {self._user.username} is not in organization.") diff --git a/Submodules/IntelOwl/api_app/connectors_manager/__init__.py b/Submodules/IntelOwl/api_app/connectors_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/connectors_manager/admin.py b/Submodules/IntelOwl/api_app/connectors_manager/admin.py new file mode 100644 index 0000000..dbb6237 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/admin.py @@ -0,0 +1,20 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.contrib import admin + +from api_app.admin import AbstractReportAdminView, PythonConfigAdminView +from api_app.connectors_manager.models import ConnectorConfig, ConnectorReport + + +@admin.register(ConnectorReport) +class ConnectorReportAdminView(AbstractReportAdminView): + ... + + +@admin.register(ConnectorConfig) +class ConnectorConfigAdminView(PythonConfigAdminView): + list_display = PythonConfigAdminView.list_display + ( + "maximum_tlp", + "run_on_failure", + ) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/apps.py b/Submodules/IntelOwl/api_app/connectors_manager/apps.py new file mode 100644 index 0000000..6f0adfe --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class ConnectorsManagerConfig(AppConfig): + name = "api_app.connectors_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/connectors_manager/classes.py b/Submodules/IntelOwl/api_app/connectors_manager/classes.py new file mode 100644 index 0000000..2e3da0a --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/classes.py @@ -0,0 +1,72 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import abc +import logging +from typing import Type + +from ..choices import PythonModuleBasePaths, ReportStatus +from ..classes import Plugin +from .exceptions import ConnectorConfigurationException, ConnectorRunException +from .models import ConnectorConfig, ConnectorReport + +logger = logging.getLogger(__name__) + + +class Connector(Plugin, metaclass=abc.ABCMeta): + """ + Abstract class for all Connectors. + Inherit from this branch when defining a connector. + Need to overrwrite `set_params(self, params: dict)` + and `run(self)` functions. + """ + + @classmethod + @property + def python_base_path(cls): + return PythonModuleBasePaths.Connector.value + + @classmethod + @property + def report_model(cls) -> Type[ConnectorReport]: + return ConnectorReport + + @classmethod + @property + def config_model(cls) -> Type[ConnectorConfig]: + return ConnectorConfig + + def get_exceptions_to_catch(self) -> list: + return [ + ConnectorConfigurationException, + ConnectorRunException, + ] + + def before_run(self): + super().before_run() + logger.info(f"STARTED connector: {self.__repr__()}") + self._config: ConnectorConfig + # an analyzer can start + # if the run_on_failure flag is set + # if there are no analyzer_reports + # it all the analyzer_reports are not failed + if ( + self._config.run_on_failure + or not self._job.analyzerreports.count() + or self._job.analyzerreports.exclude( + status=ReportStatus.FAILED.value + ).exists() + ): + logger.info( + f"Running connector {self.__class__.__name__} " + f"even if job status is {self._job.status} because" + "run on failure is set" + ) + else: + raise ConnectorRunException( + "An analyzer has failed," + f" unable to run connector {self.__class__.__name__}" + ) + + def after_run(self): + super().after_run() + logger.info(f"FINISHED connector: {self.__repr__()}") diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/__init__.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/abuse_submitter.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/abuse_submitter.py new file mode 100644 index 0000000..98846e6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/connectors/abuse_submitter.py @@ -0,0 +1,18 @@ +from api_app.connectors_manager.connectors.email_sender import EmailSender + + +class AbuseSubmitter(EmailSender): + @property + def subject(self) -> str: + return ( + "Takedown domain request for " + f"{self._job.parent_job.parent_job.observable_name}" + ) + + @property + def body(self) -> str: + return ( + f"Domain {self._job.parent_job.parent_job.observable_name} " + "has been detected as malicious by our team. We kindly request you to take " + "it down as soon as possible." + ) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/email_sender.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/email_sender.py new file mode 100644 index 0000000..ce6d58a --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/connectors/email_sender.py @@ -0,0 +1,52 @@ +from django.core.mail import EmailMessage + +from api_app.connectors_manager.classes import Connector +from intel_owl.settings import DEFAULT_FROM_EMAIL +from tests.mock_utils import if_mock_connections, patch + + +class EmailSender(Connector): + sender: str + subject: str + header: str + body: str + footer: str + + def run(self) -> dict: + if hasattr(self, "sender") and self.sender: + sender = self.sender + else: + sender = DEFAULT_FROM_EMAIL + body = self.body + if hasattr(self, "header") and self.header: + body = self.header + "\n\n" + body + if hasattr(self, "footer") and self.footer: + body = body + "\n\n" + self.footer + base_eml = EmailMessage( + subject=self.subject, + from_email=sender, + to=[self._job.observable_name], + body=body, + ) + base_eml.send() + return { + "subject": base_eml.subject, + "from": base_eml.from_email, + "to": base_eml.to, + "body": base_eml.body, + } + + def update(self) -> bool: + pass + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "django.core.mail.EmailMessage.send", + return_value="Email sent", + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/misp.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/misp.py new file mode 100644 index 0000000..377d162 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/connectors/misp.py @@ -0,0 +1,172 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from typing import List + +import pymisp +from django.conf import settings + +from api_app import helpers +from api_app.analyzers_manager.constants import ObservableTypes +from api_app.connectors_manager.classes import Connector +from tests.mock_utils import if_mock_connections, patch + +INTELOWL_MISP_TYPE_MAP = { + ObservableTypes.IP: "ip-src", + ObservableTypes.DOMAIN: "domain", + ObservableTypes.URL: "url", + # "hash" (checked from helpers.get_hash_type) + ObservableTypes.GENERIC: "text", # misc field, so keeping text + "file": "filename|md5", +} + + +def create_misp_attribute(misp_type, misp_value) -> pymisp.MISPAttribute: + obj = pymisp.MISPAttribute() + obj.type = misp_type + obj.value = misp_value + return obj + + +class MISP(Connector): + tlp: str + ssl_check: bool + self_signed_certificate: str + debug: bool + _api_key_name: str + _url_key_name: str + + @property + def _event_obj(self) -> pymisp.MISPEvent: + obj = pymisp.MISPEvent() + obj.info = f"Intelowl Job-{self.job_id}" + obj.distribution = 0 # your_organisation_only + obj.threat_level_id = 4 # undefined + obj.analysis = 2 # completed + obj.add_tag("source:intelowl") + obj.add_tag(f"tlp:{self.tlp}") # tlp tag for sharing + + # Add tags from Job + for tag in self._job.tags.all(): + obj.add_tag(f"intelowl-tag:{tag.label}") + + return obj + + @property + def _base_attr_obj(self) -> pymisp.MISPAttribute: + if self._job.is_sample: + _type = INTELOWL_MISP_TYPE_MAP["file"] + value = f"{self._job.file_name}|{self._job.md5}" + else: + _type = self._job.observable_classification + value = self._job.observable_name + if _type == ObservableTypes.HASH: + matched_type = helpers.get_hash_type(value) + matched_type.replace("-", "") # convert sha-x to shax + _type = matched_type if matched_type is not None else "text" + else: + _type = INTELOWL_MISP_TYPE_MAP[_type] + + obj = create_misp_attribute(_type, value) + analyzers_names = self._job.analyzers_to_execute.all().values_list( + "name", flat=True + ) + obj.comment = "Analyzers Executed:" f" {', '.join(analyzers_names)}" + return obj + + @property + def _secondary_attr_objs(self) -> List[pymisp.MISPAttribute]: + obj_list = [] + if self._job.is_sample: + # mime-type + obj_list.append(create_misp_attribute("mime-type", self._job.file_mimetype)) + return obj_list + + @property + def _link_attr_obj(self) -> pymisp.MISPAttribute: + """ + Returns attribute linking analysis on IntelOwl instance + """ + obj = pymisp.MISPAttribute() + obj.type = "link" + obj.value = f"{settings.WEB_CLIENT_URL}/jobs/{self.job_id}" + obj.comment = "View Analysis on IntelOwl" + obj.disable_correlation = True + + return obj + + def run(self): + ssl_param = ( + f"{settings.PROJECT_LOCATION}/configuration/misp_ssl.crt" + if self.ssl_check and self.self_signed_certificate + else self.ssl_check + ) + misp_instance = pymisp.PyMISP( + url=self._url_key_name, + key=self._api_key_name, + ssl=ssl_param, + debug=self.debug, + timeout=5, + ) + + # get event and attributes + event = self._event_obj + attributes = [ + self._base_attr_obj, + *self._secondary_attr_objs, + self._link_attr_obj, + ] + + # append attribute name to event info + event.info += f": {self._base_attr_obj.value}" + + # add event to MISP Instance + misp_event = misp_instance.add_event(event, pythonify=True) + # add attributes to event on MISP Instance + for attr in attributes: + misp_instance.add_attribute(misp_event.id, attr) + + return misp_instance.get_event(misp_event.id) + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "pymisp.PyMISP", + side_effect=MockPyMISP, + ) + ) + ] + return super()._monkeypatch(patches=patches) + + +# Mocks +class MockUpMISPElement: + """ + Mock element(event/attribute) for testing + """ + + id: int = 1 + + +class MockPyMISP: + """ + Mock PyMISP instance for testing + methods which require connection to a MISP instance + """ + + def __init__(self, *args, **kwargs) -> None: + pass + + @staticmethod + def add_event(*args, **kwargs) -> MockUpMISPElement: + return MockUpMISPElement() + + @staticmethod + def add_attribute(*args, **kwargs) -> MockUpMISPElement: + return MockUpMISPElement() + + @staticmethod + def get_event(event_id) -> dict: + return {"Event": {"id": event_id}} diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/opencti.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/opencti.py new file mode 100644 index 0000000..000db12 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/connectors/opencti.py @@ -0,0 +1,208 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from typing import Dict + +import pycti +from django.conf import settings +from pycti.api.opencti_api_client import File + +from api_app import helpers +from api_app.analyzers_manager.constants import ObservableTypes +from api_app.connectors_manager import classes +from tests.mock_utils import if_mock_connections, patch + +INTELOWL_OPENCTI_TYPE_MAP = { + ObservableTypes.IP: { + "v4": "ipv4-addr", + "v6": "ipv6-addr", + }, + ObservableTypes.DOMAIN: "domain-name", + ObservableTypes.URL: "url", + # type hash is missing because it is combined with "file" + # "generic" is misc field, so keeping text + ObservableTypes.GENERIC: "x-opencti-text", + "file": "file", # hashes: md5, sha-1, sha-256 +} + + +class OpenCTI(classes.Connector): + ssl_verify: bool + tlp: dict + proxies: str + _url_key_name: str + _api_key_name: str + + def get_observable_type(self) -> str: + if self._job.is_sample: + obs_type = INTELOWL_OPENCTI_TYPE_MAP["file"] + elif self._job.observable_classification == ObservableTypes.HASH: + matched_hash_type = helpers.get_hash_type(self._job.observable_name) + if matched_hash_type in [ + "md5", + "sha-1", + "sha-256", + ]: # sha-512 not supported + obs_type = INTELOWL_OPENCTI_TYPE_MAP["file"] + else: + obs_type = INTELOWL_OPENCTI_TYPE_MAP[ObservableTypes.GENERIC] # text + elif self._job.observable_classification == ObservableTypes.IP: + ip_version = helpers.get_ip_version(self._job.observable_name) + if ip_version in [4, 6]: + obs_type = INTELOWL_OPENCTI_TYPE_MAP[ObservableTypes.IP][ + f"v{ip_version}" + ] # v4/v6 + else: + obs_type = INTELOWL_OPENCTI_TYPE_MAP[ObservableTypes.GENERIC] # text + else: + obs_type = INTELOWL_OPENCTI_TYPE_MAP[self._job.observable_classification] + + return obs_type + + def generate_observable_data(self) -> dict: + observable_data = {"type": self.get_observable_type()} + if self._job.is_sample: + observable_data["name"] = self._job.file_name + observable_data["hashes"] = { + "md5": self._job.md5, + "sha-1": self._job.sha1, + "sha-256": self._job.sha256, + } + elif ( + self._job.observable_classification == ObservableTypes.HASH + and observable_data["type"] == "file" + ): + # add hash instead of value + matched_type = helpers.get_hash_type(self._job.observable_name) + observable_data["hashes"] = {matched_type: self._job.observable_name} + else: + observable_data["value"] = self._job.observable_name + + return observable_data + + @property + def organization_id(self) -> str: + # Create author (if not exists); else update + org = pycti.Identity(self.opencti_instance).create( + type="Organization", + name="IntelOwl", + description=( + "Intel Owl is an Open Source Intelligence, or OSINT solution" + " to get threat intelligence data about a specific file, an IP or a" + " domain from a single API at scale. [Visit the project on GitHub]" + "(https://github.com/intelowlproject/IntelOwl/)" + ), + update=True, # just in case the description is updated in future + ) + return org["id"] + + @property + def marking_definition_id(self) -> str: + # Create the marking definition (if not exists) + md = pycti.MarkingDefinition(self.opencti_instance).create( + definition_type="TLP", + definition=f"TLP:{self.tlp['type'].upper()}", + x_opencti_color=self.tlp["color"].lower(), + x_opencti_order=self.tlp["x_opencti_order"], + ) + return md["id"] + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + if self.ssl_verify is None: + self.ssl_verify = False + + def run(self): + # set up client + self.opencti_instance = pycti.OpenCTIApiClient( + url=self._url_key_name, + token=self._api_key_name, + ssl_verify=self.ssl_verify, + proxies=self.proxies, + ) + + # Entities in OpenCTI are created only if they don't exist + # create queries will return the existing entity in that case + # use update (default: False) to update the entity if exists + + # Create the observable (if not exists with the given type and values) + observable_data = self.generate_observable_data() + observable = pycti.StixCyberObservable(self.opencti_instance, File).create( + observableData=observable_data, + createdBy=self.organization_id, + objectMarking=self.marking_definition_id, + ) + + # Create labels from Job tags (if not exists) + label_ids = [] + for tag in self._job.tags.all(): + label = pycti.Label(self.opencti_instance).create( + value=f"intelowl-tag:{tag.label}", + color=tag.color, + ) + label_ids.append(label["id"]) + + # Create the report + report = pycti.Report(self.opencti_instance).create( + name=f"IntelOwl Job-{self.job_id}", + description=( + f"This is IntelOwl's analysis report for Job: {self.job_id}." + # comma separate analyzers executed + " Analyzers Executed:" + f" {', '.join(list(self._job.analyzers_to_execute.all().values_list('name', flat=True)))}" # noqa + ), + published=self._job.received_request_time.strftime("%Y-%m-%dT%H:%M:%SZ"), + report_types=["internal-report"], + createdBy=self.organization_id, + objectMarking=self.marking_definition_id, + objectLabel=label_ids, + x_opencti_report_status=2, # Analyzed + ) + # Create the external reference + external_reference = pycti.ExternalReference( + self.opencti_instance, None + ).create( + source_name="IntelOwl Analysis", + description="View analysis report on the IntelOwl instance", + url=f"{settings.WEB_CLIENT_URL}/jobs/{self.job_id}", + ) + # Add the external reference to the report + pycti.StixDomainObject(self.opencti_instance, File).add_external_reference( + id=report["id"], external_reference_id=external_reference["id"] + ) + + # Link Observable and Report + pycti.Report(self.opencti_instance).add_stix_object_or_stix_relationship( + id=report["id"], stixObjectOrStixRelationshipId=observable["id"] + ) + + return { + "observable": pycti.StixCyberObservable(self.opencti_instance, File).read( + id=observable["id"] + ), + "report": pycti.Report(self.opencti_instance).read(id=report["id"]), + } + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch("pycti.OpenCTIApiClient", return_value=None), + patch("pycti.Identity.create", return_value={"id": 1}), + patch("pycti.MarkingDefinition.create", return_value={"id": 1}), + patch("pycti.StixCyberObservable.create", return_value={"id": 1}), + patch("pycti.Label.create", return_value={"id": 1}), + patch("pycti.Report.create", return_value={"id": 1}), + patch("pycti.ExternalReference.create", return_value={"id": 1}), + patch( + "pycti.StixDomainObject.add_external_reference", return_value=None + ), + patch( + "pycti.Report.add_stix_object_or_stix_relationship", + return_value=None, + ), + patch("pycti.StixCyberObservable.read", return_value={"id": 1}), + patch("pycti.Report.read", return_value={"id": 1}), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/slack.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/slack.py new file mode 100644 index 0000000..0e07d97 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/connectors/slack.py @@ -0,0 +1,59 @@ +from typing import Dict +from unittest.mock import patch + +import slack_sdk +from slack_sdk.errors import SlackApiError + +from api_app.connectors_manager.classes import Connector +from tests.mock_utils import if_mock_connections + + +class Slack(Connector): + _channel: str + slack_username: str + _token: str + + def get_exceptions_to_catch(self) -> list: + elems = super().get_exceptions_to_catch() + return elems + [SlackApiError] + + def config(self, runtime_configuration: Dict): + super().config(runtime_configuration) + self.client = slack_sdk.WebClient(token=self._token) + + @property + def title(self) -> str: + return "*IntelOwl analysis*" + + @property + def body(self) -> str: + return ( + "Analysis executed " + f"{f'by <@{self.slack_username}> ' if self.slack_username else ''}" + f"for <{self._job.url}/raw|{self._job.analyzed_object_name}>" + ) + + def run(self) -> dict: + self.client.chat_postMessage( + text=f"{self.title}\n{self.body}", channel=self._channel, mrkdwn=True + ) + return {} + + @classmethod + def _monkeypatch(cls): + class MockClient: + def __init__(self, *args, **kwargs): + ... + + def chat_postMessage(self, *args, **kwargs): + ... + + patches = [ + if_mock_connections( + patch( + "slack_sdk.WebClient", + side_effect=MockClient, + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/connectors/yeti.py b/Submodules/IntelOwl/api_app/connectors_manager/connectors/yeti.py new file mode 100644 index 0000000..f9bdfbd --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/connectors/yeti.py @@ -0,0 +1,81 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import requests +from django.conf import settings + +from api_app.connectors_manager import classes +from api_app.connectors_manager.exceptions import ConnectorRunException +from tests.mock_utils import MockUpResponse, if_mock_connections, patch + + +class YETI(classes.Connector): + verify_ssl: bool + _url_key_name: str + _api_key_name: str + + def run(self): + # get observable value and type + if self._job.is_sample: + obs_value = self._job.md5 + obs_type = "file" + else: + obs_value = self._job.observable_name + obs_type = self._job.observable_classification + + # create context + context = { + "source": "IntelOwl", + "report": f"{settings.WEB_CLIENT_URL}/jobs/{self.job_id}", + "status": "analyzed", + "date": str(self._job.finished_analysis_time), + "description": "IntelOwl's analysis report for Job: " + f"{self.job_id} | {obs_value} | {obs_type}", + "analyzers executed": ", ".join( + list( + self._job.analyzers_to_execute.all().values_list("name", flat=True) + ) + ), + } + + # get job tags + tags = list(self._job.tags.all().values_list("label", flat=True)) + + # request payload + payload = { + "value": obs_value, + "source": "IntelOwl", + "tags": tags, + "context": context, + } + headers = {"Accept": "application/json", "X-Api-Key": self._api_key_name} + if self._url_key_name and self._url_key_name.endswith("/"): + self._url_key_name = self._url_key_name[:-1] + url = f"{self._url_key_name}/api/v2/observables/" + + # create observable with `obs_value` if it doesn't exists + # new context, tags, source are appended with existing ones + try: + resp = requests.post( + url=url, + headers=headers, + json=payload, + verify=self.verify_ssl, + ) + resp.raise_for_status() + except requests.RequestException as e: + raise ConnectorRunException(e) + + return resp.json() + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse({}, 200), + ) + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/exceptions.py b/Submodules/IntelOwl/api_app/connectors_manager/exceptions.py new file mode 100644 index 0000000..5954ad5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/exceptions.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +class NotRunnableConnector(Exception): + pass + + +class ConnectorConfigurationException(Exception): + pass + + +class ConnectorRunException(Exception): + pass diff --git a/Submodules/IntelOwl/api_app/connectors_manager/forms.py b/Submodules/IntelOwl/api_app/connectors_manager/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0001_initial_squashed.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0001_initial_squashed.py new file mode 100644 index 0000000..a8802d2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0001_initial_squashed.py @@ -0,0 +1,215 @@ +# Generated by Django 4.2.8 on 2024-02-08 10:53 + +import django.contrib.postgres.fields +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + replaces = [ + # ("connectors_manager", "0001_initial"), + # ("connectors_manager", "0002_connectorreport_parent_playbook"), + # ("connectors_manager", "0003_connectorconfig"), + # ("connectors_manager", "0004_datamigration"), + # ("connectors_manager", "0005_auto_20230301_1415"), + # ("connectors_manager", "0006_connectorconfig_disabled_in_org"), + # ("connectors_manager", "0007_alter_connectorreport_job"), + # ("connectors_manager", "0008_auto_20230308_1623"), + # ("connectors_manager", "0009_parent_playbook_foreign_key"), + # ("connectors_manager", "0010_remove_parent_playbook"), + # ("connectors_manager", "00011_remove_runtime_configuration"), + # ("connectors_manager", "0012_slack"), + # ("connectors_manager", "0013_tlp_clear"), + # ( + # "connectors_manager", + # "0014_alter_connectorconfig_disabled_in_organizations_and_more", + # ), + # ("connectors_manager", "0015_params"), + # ("connectors_manager", "0016_alter_connectorconfig_name"), + # ("connectors_manager", "0017_alter_connectorconfig_options"), + # ("connectors_manager", "0018_alter_connectorconfig_name"), + # ( + # "connectors_manager", + # "0019_rename_connectors__python__0fb146_idx_connectors__python__f23fd8_idx_and_more", + # ), + # ("connectors_manager", "0020_alter_python_module"), + # ("connectors_manager", "0021_alter_connectorconfig_python_module"), + # ("connectors_manager", "0022_alter_connectorconfig_python_module"), + # ("connectors_manager", "0023_connectorconfig_routing_key_and_more"), + # ("connectors_manager", "0024_connectorconfig_health_check_task"), + # ("connectors_manager", "0025_connectorconfig_health_check_status"), + # ("connectors_manager", "0026_connectorreport_parameters"), + # ("connectors_manager", "0027_connectorreport_sent_to_bi"), + # ("connectors_manager", "0028_connectorreport_connectorreportsbisearch"), + ] + + dependencies = [("api_app", "0001_1_initial_squashed")] + operations = [ + migrations.CreateModel( + name="ConnectorConfig", + fields=[ + ( + "name", + models.CharField( + max_length=100, + primary_key=True, + serialize=False, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", + "Your name should match the [A-Za-z0-9_] characters", + ) + ], + ), + ), + ( + "python_module", + models.ForeignKey( + limit_choices_to={ + "base_path": "api_app.connectors_manager.connectors" + }, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)ss", + to="api_app.pythonmodule", + ), + ), + ("description", models.TextField()), + ("disabled", models.BooleanField(default=False)), + ( + "disabled_in_organizations", + models.ManyToManyField( + blank=True, + related_name="%(app_label)s_%(class)s_disabled", + to="certego_saas_organization.organization", + ), + ), + ( + "health_check_status", + models.BooleanField(default=True, editable=False), + ), + ( + "health_check_task", + models.OneToOneField( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="healthcheck_for_%(class)s", + to="django_celery_beat.periodictask", + ), + ), + ( + "soft_time_limit", + models.IntegerField( + default=60, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ("routing_key", models.CharField(default="default", max_length=50)), + ( + "maximum_tlp", + models.CharField( + choices=[ + ("CLEAR", "Clear"), + ("GREEN", "Green"), + ("AMBER", "Amber"), + ("RED", "Red"), + ], + default="CLEAR", + max_length=50, + ), + ), + ("run_on_failure", models.BooleanField(default=True)), + ], + options={"abstract": False, "ordering": ["name", "disabled"]}, + ), + migrations.AddIndex( + model_name="connectorconfig", + index=models.Index( + fields=["python_module", "disabled"], + name="connectors__python__0fb146_idx", + ), + ), + migrations.RenameIndex( + model_name="connectorconfig", + new_name="connectors__python__f23fd8_idx", + old_name="connectors__python__0fb146_idx", + ), + migrations.CreateModel( + name="ConnectorReport", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("FAILED", "Failed"), + ("PENDING", "Pending"), + ("RUNNING", "Running"), + ("SUCCESS", "Success"), + ("KILLED", "Killed"), + ], + max_length=50, + ), + ), + ("report", models.JSONField(default=dict)), + ( + "errors", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=512), + blank=True, + default=list, + size=None, + ), + ), + ("start_time", models.DateTimeField(default=django.utils.timezone.now)), + ("end_time", models.DateTimeField(default=django.utils.timezone.now)), + ("task_id", models.UUIDField()), + ( + "job", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_app.job", + ), + ), + ("sent_to_bi", models.BooleanField(default=False, editable=False)), + ("parameters", models.JSONField(default={}, editable=False)), + ( + "config", + models.ForeignKey( + "ConnectorConfig", + related_name="reports", + on_delete=models.CASCADE, + ), + ), + ], + ), + migrations.AddIndex( + model_name="connectorreport", + index=models.Index( + fields=["sent_to_bi", "-start_time"], name="connectorreportsBISearch" + ), + ), + migrations.AlterUniqueTogether( + name="connectorreport", + unique_together={("config", "job")}, + ), + migrations.AlterField( + model_name="connectorreport", + name="parameters", + field=models.JSONField(editable=False), + ), + ] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0000_connector_config_misp.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0000_connector_config_misp.py new file mode 100644 index 0000000..09c9013 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0000_connector_config_misp.py @@ -0,0 +1,253 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "MISP", + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "description": "Automatically creates an event on your MISP instance, linking the successful analysis on IntelOwl", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "maximum_tlp": "CLEAR", + "run_on_failure": False, + "health_check_task": None, + "model": "connectors_manager.ConnectorConfig", +} + +params = [ + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "tlp", + "type": "str", + "description": "Change this as per your organization's threat sharing conventions.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `/configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for your MISP instance", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "url_key_name", + "type": "str", + "description": "URL of your MISP instance", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "tlp", + "type": "str", + "description": "Change this as per your organization's threat sharing conventions.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "clear", + "updated_at": "2024-02-09T10:52:16.102464Z", + "owner": None, + "analyzer_config": None, + "connector_config": "MISP", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "debug", + "type": "bool", + "description": "Enable debug logs.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:16.116031Z", + "owner": None, + "analyzer_config": None, + "connector_config": "MISP", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "ssl_check", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your MISP instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.129283Z", + "owner": None, + "analyzer_config": None, + "connector_config": "MISP", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "misp.MISP", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "self_signed_certificate", + "type": "bool", + "description": "If ssl_check and this flag are True, the analyzer will leverage a CA_BUNDLE to authenticate against the MISP instance. IntelOwl will look for it at this path: `/configuration/misp_ssl.crt`. Remember that this file should be readable by the application (`www-data` user must read this)", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": False, + "updated_at": "2024-02-09T10:52:16.144268Z", + "owner": None, + "analyzer_config": None, + "connector_config": "MISP", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("connectors_manager", "0001_initial_squashed"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0001_connector_config_opencti.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0001_connector_config_opencti.py new file mode 100644 index 0000000..31a2eb9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0001_connector_config_opencti.py @@ -0,0 +1,220 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "OpenCTI", + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "description": "Automatically creates an observable and a linked report on your OpenCTI instance, linking the successful analysis on IntelOwl. CARE! This may require additional advanced configuration. Check the docs [here](https://intelowl.readthedocs.io/en/latest/Advanced-Configuration.html#opencti)", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "maximum_tlp": "CLEAR", + "run_on_failure": False, + "health_check_task": None, + "model": "connectors_manager.ConnectorConfig", +} + +params = [ + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "tlp", + "type": "dict", + "description": "Change this as per your organization's threat sharing conventions.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "proxies", + "type": "dict", + "description": "Use these options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "ssl_verify", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your OpenCTI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for your OpenCTI instance", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "url_key_name", + "type": "str", + "description": "URL of your OpenCTI instance", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "tlp", + "type": "dict", + "description": "Change this as per your organization's threat sharing conventions.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": {"type": "white", "color": "#ffffff", "x_opencti_order": 1}, + "updated_at": "2024-02-09T10:52:16.203594Z", + "owner": None, + "analyzer_config": None, + "connector_config": "OpenCTI", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "proxies", + "type": "dict", + "description": "Use these options to pass your request through a proxy server.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": {"http": "", "https": ""}, + "updated_at": "2024-02-09T10:52:16.219704Z", + "owner": None, + "analyzer_config": None, + "connector_config": "OpenCTI", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, + { + "parameter": { + "python_module": { + "module": "opencti.OpenCTI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "ssl_verify", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your OpenCTI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.233159Z", + "owner": None, + "analyzer_config": None, + "connector_config": "OpenCTI", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + }, +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("connectors_manager", "0002_0000_connector_config_misp"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0002_connector_config_slack.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0002_connector_config_slack.py new file mode 100644 index 0000000..6e292d2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0002_connector_config_slack.py @@ -0,0 +1,154 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Slack", + "python_module": { + "module": "slack.Slack", + "base_path": "api_app.connectors_manager.connectors", + }, + "description": "Send the analysis link to a slack channel", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "maximum_tlp": "RED", + "run_on_failure": True, + "health_check_task": None, + "model": "connectors_manager.ConnectorConfig", +} + +params = [ + { + "python_module": { + "module": "slack.Slack", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "slack_username", + "type": "str", + "description": "Slack username to tag on the message", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "slack.Slack", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "token", + "type": "str", + "description": "Slack token for authentication", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "slack.Slack", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "channel", + "type": "str", + "description": "Slack channel to send messages", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "slack.Slack", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "slack_username", + "type": "str", + "description": "Slack username to tag on the message", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": "", + "updated_at": "2024-02-09T10:52:16.287639Z", + "owner": None, + "analyzer_config": None, + "connector_config": "Slack", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("connectors_manager", "0002_0001_connector_config_opencti"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0003_connector_config_yeti.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0003_connector_config_yeti.py new file mode 100644 index 0000000..01856fb --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0002_0003_connector_config_yeti.py @@ -0,0 +1,154 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "YETI", + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.connectors_manager.connectors", + }, + "description": "find or create observable on YETI, linking the successful analysis on IntelOwl.", + "disabled": False, + "soft_time_limit": 30, + "routing_key": "default", + "health_check_status": True, + "maximum_tlp": "CLEAR", + "run_on_failure": True, + "health_check_task": None, + "model": "connectors_manager.ConnectorConfig", +} + +params = [ + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "verify_ssl", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your YETI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "api_key_name", + "type": "str", + "description": "API key for your YETI instance", + "is_secret": True, + "required": True, + }, + { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "url_key_name", + "type": "str", + "description": "API URL of your YETI instance", + "is_secret": True, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "yeti.YETI", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "verify_ssl", + "type": "bool", + "description": "Enable SSL certificate server verification. Change this if your YETI instance has not SSL enabled.", + "is_secret": False, + "required": False, + }, + "for_organization": False, + "value": True, + "updated_at": "2024-02-09T10:52:16.260168Z", + "owner": None, + "analyzer_config": None, + "connector_config": "YETI", + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("connectors_manager", "0002_0002_connector_config_slack"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_1_change_primary_key.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_1_change_primary_key.py new file mode 100644 index 0000000..b13f51c --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_1_change_primary_key.py @@ -0,0 +1,12 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("connectors_manager", "0002_0003_connector_config_yeti"), + ("playbooks_manager", "0001_initial_squashed"), + ("api_app", "0001_2_initial_squashed"), + ] + + operations = [] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_2_change_primary_key.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_2_change_primary_key.py new file mode 100644 index 0000000..4f831b9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_2_change_primary_key.py @@ -0,0 +1,45 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + ConnectorConfig = apps.get_model("connectors_manager", "ConnectorConfig") + ConnectorConfig.objects.update( + disabled2=ArraySubquery( + ConnectorConfig.objects.filter(pk=models.OuterRef("pk")).values( + "disabled_in_organizations__pk" + ) + ) + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("connectors_manager", "0029_1_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="connectorconfig", + name="disabled2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.IntegerField(), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython( + migrate, + ), + migrations.AlterField( + model_name="connectorreport", + name="config", + field=models.CharField(max_length=100, null=False, blank=False), + ), + migrations.RemoveField( + model_name="connectorconfig", name="disabled_in_organizations" + ), + ] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_3_change_primary_key.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_3_change_primary_key.py new file mode 100644 index 0000000..2ed6288 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_3_change_primary_key.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("connectors_manager", "0029_2_change_primary_key"), + ("api_app", "0057_2_change_primary_key"), + ("playbooks_manager", "0023_2_change_primary_key"), + ("pivots_manager", "0023_2_change_primary_key"), + ] + + operations = [ + migrations.RunSQL( + 'ALTER TABLE "connectors_manager_connectorconfig" DROP CONSTRAINT "connectors_manager_connectorconfig_pkey" CASCADE;' + ), + migrations.AlterField( + model_name="connectorconfig", + name="name", + field=models.CharField( + max_length=100, + unique=True, + primary_key=False, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", "Your name should match the [A-Za-z0-9_] characters" + ) + ], + ), + ), + migrations.AddField( + model_name="connectorconfig", + name="id", + field=models.BigAutoField( + auto_created=True, serialize=False, verbose_name="ID", primary_key=True + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_4_change_primary_key.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_4_change_primary_key.py new file mode 100644 index 0000000..5c1efb4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0029_4_change_primary_key.py @@ -0,0 +1,69 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + ConnectorReport = apps.get_model("connectors_manager", "ConnectorReport") + ConnectorConfig = apps.get_model("connectors_manager", "ConnectorConfig") + Organization = apps.get_model("certego_saas_organization", "Organization") + name = ConnectorConfig.objects.filter( + name=models.OuterRef("old_config") + ).values_list("pk")[:1] + ConnectorReport.objects.update(config=models.Subquery(name)) + for config in ConnectorConfig.objects.all(): + if config.disabled2: + ContentType = apps.get_model("contenttypes", "ContentType") + ct = ContentType.objects.get_for_model(config) + OrganizationPluginConfiguration = apps.get_model( + "api_app", "OrganizationPluginConfiguration" + ) + for org in config.disabled2: + if org: + OrganizationPluginConfiguration.objects.create( + organization=Organization.objects.get(pk=org), + object_id=config.pk, + content_type=ct, + disabled=True, + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("connectors_manager", "0029_3_change_primary_key"), + ] + + operations = [ + migrations.RenameField( + model_name="connectorreport", old_name="config", new_name="old_config" + ), + migrations.AddField( + model_name="connectorreport", + name="config", + field=models.ForeignKey( + default=None, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="connectors_manager.connectorconfig", + ), + preserve_default=False, + ), + migrations.RunPython(migrate), + migrations.AlterUniqueTogether( + name="connectorreport", + unique_together={("config", "job")}, + ), + migrations.AlterField( + model_name="connectorreport", + name="config", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="connectors_manager.connectorconfig", + ), + ), + migrations.RemoveField(model_name="connectorconfig", name="disabled2"), + migrations.RemoveField(model_name="connectorreport", name="old_config"), + ] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0030_connector_config_emailsender.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0030_connector_config_emailsender.py new file mode 100644 index 0000000..08d89f3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0030_connector_config_emailsender.py @@ -0,0 +1,167 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "email_sender.EmailSender", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "EmailSender", + "description": "Send a generic email", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "maximum_tlp": "AMBER", + "run_on_failure": True, + "model": "connectors_manager.ConnectorConfig", +} + +params = [ + { + "python_module": { + "module": "email_sender.EmailSender", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "subject", + "type": "str", + "description": "Email subject", + "is_secret": False, + "required": True, + }, + { + "python_module": { + "module": "email_sender.EmailSender", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "header", + "type": "str", + "description": "Email header for initial greetings", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "email_sender.EmailSender", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "body", + "type": "str", + "description": "Email body", + "is_secret": False, + "required": True, + }, + { + "python_module": { + "module": "email_sender.EmailSender", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "footer", + "type": "str", + "description": "Email footer for final greetings", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "email_sender.EmailSender", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "sender", + "type": "str", + "description": "Email sender", + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("connectors_manager", "0029_4_change_primary_key"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/0031_connector_config_abusesubmitter.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0031_connector_config_abusesubmitter.py new file mode 100644 index 0000000..dc20e9a --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/migrations/0031_connector_config_abusesubmitter.py @@ -0,0 +1,145 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "abuse_submitter.AbuseSubmitter", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "AbuseSubmitter", + "description": "Send an email to request to take down a malicious domain.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "maximum_tlp": "AMBER", + "run_on_failure": True, + "model": "connectors_manager.ConnectorConfig", +} + +params = [ + { + "python_module": { + "module": "abuse_submitter.AbuseSubmitter", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "header", + "type": "str", + "description": "Email header for initial greetings", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "abuse_submitter.AbuseSubmitter", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "footer", + "type": "str", + "description": "Email footer for final greetings", + "is_secret": False, + "required": False, + }, + { + "python_module": { + "module": "abuse_submitter.AbuseSubmitter", + "base_path": "api_app.connectors_manager.connectors", + }, + "name": "sender", + "type": "str", + "description": "Email sender", + "is_secret": False, + "required": False, + }, +] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("connectors_manager", "0030_connector_config_emailsender"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/connectors_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/connectors_manager/models.py b/Submodules/IntelOwl/api_app/connectors_manager/models.py new file mode 100644 index 0000000..cef6ca5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/models.py @@ -0,0 +1,53 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from django.contrib.contenttypes.fields import GenericRelation +from django.db import models + +from api_app.choices import TLP, PythonModuleBasePaths +from api_app.connectors_manager.exceptions import ConnectorConfigurationException +from api_app.connectors_manager.queryset import ConnectorReportQuerySet +from api_app.models import AbstractReport, PythonConfig, PythonModule + + +class ConnectorReport(AbstractReport): + objects = ConnectorReportQuerySet.as_manager() + config = models.ForeignKey( + "ConnectorConfig", related_name="reports", null=False, on_delete=models.CASCADE + ) + + class Meta: + unique_together = [("config", "job")] + indexes = AbstractReport.Meta.indexes + + +class ConnectorConfig(PythonConfig): + maximum_tlp = models.CharField( + null=False, default=TLP.CLEAR, choices=TLP.choices, max_length=50 + ) + run_on_failure = models.BooleanField(null=False, default=True) + python_module = models.ForeignKey( + PythonModule, + on_delete=models.PROTECT, + related_name="%(class)ss", + limit_choices_to={"base_path": PythonModuleBasePaths.Connector.value}, + ) + orgs_configuration = GenericRelation( + "api_app.OrganizationPluginConfiguration", related_name="%(class)s" + ) + + @classmethod + @property + def plugin_type(cls) -> str: + return "2" + + @classmethod + @property + def config_exception(cls): + return ConnectorConfigurationException + + @classmethod + @property + def serializer_class(cls): + from api_app.connectors_manager.serializers import ConnectorConfigSerializer + + return ConnectorConfigSerializer diff --git a/Submodules/IntelOwl/api_app/connectors_manager/queryset.py b/Submodules/IntelOwl/api_app/connectors_manager/queryset.py new file mode 100644 index 0000000..bcc969a --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/queryset.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING, Type + +from api_app.queryset import AbstractReportQuerySet + +if TYPE_CHECKING: + from api_app.connectors_manager.serializers import ConnectorReportBISerializer + + +class ConnectorReportQuerySet(AbstractReportQuerySet): + @classmethod + def _get_bi_serializer_class(cls) -> Type["ConnectorReportBISerializer"]: + from api_app.connectors_manager.serializers import ConnectorReportBISerializer + + return ConnectorReportBISerializer diff --git a/Submodules/IntelOwl/api_app/connectors_manager/serializers.py b/Submodules/IntelOwl/api_app/connectors_manager/serializers.py new file mode 100644 index 0000000..cda9714 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/serializers.py @@ -0,0 +1,36 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from ..serializers.plugin import ( + PythonConfigSerializer, + PythonConfigSerializerForMigration, +) +from ..serializers.report import AbstractReportBISerializer, AbstractReportSerializer +from .models import ConnectorConfig, ConnectorReport + + +class ConnectorConfigSerializer(PythonConfigSerializer): + class Meta: + model = ConnectorConfig + exclude = PythonConfigSerializer.Meta.exclude + list_serializer_class = PythonConfigSerializer.Meta.list_serializer_class + + +class ConnectorConfigSerializerForMigration(PythonConfigSerializerForMigration): + class Meta: + model = ConnectorConfig + exclude = PythonConfigSerializerForMigration.Meta.exclude + + +class ConnectorReportSerializer(AbstractReportSerializer): + class Meta: + model = ConnectorReport + fields = AbstractReportSerializer.Meta.fields + list_serializer_class = AbstractReportSerializer.Meta.list_serializer_class + + +class ConnectorReportBISerializer(AbstractReportBISerializer): + class Meta: + model = ConnectorReport + fields = AbstractReportBISerializer.Meta.fields + list_serializer_class = AbstractReportBISerializer.Meta.list_serializer_class diff --git a/Submodules/IntelOwl/api_app/connectors_manager/signals.py b/Submodules/IntelOwl/api_app/connectors_manager/signals.py new file mode 100644 index 0000000..789d966 --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/signals.py @@ -0,0 +1,31 @@ +import logging +import uuid + +from django.conf import settings +from django.dispatch import receiver + +from api_app.connectors_manager.models import ConnectorConfig +from api_app.signals import migrate_finished +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +@receiver(migrate_finished) +def post_migrate_connectors_manager( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from intel_owl.tasks import refresh_cache + + refresh_cache.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=3, + args=[ConnectorConfig.python_path], + ) diff --git a/Submodules/IntelOwl/api_app/connectors_manager/urls.py b/Submodules/IntelOwl/api_app/connectors_manager/urls.py new file mode 100644 index 0000000..b23058e --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/urls.py @@ -0,0 +1,20 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +from .views import ConnectorActionViewSet, ConnectorConfigViewSet + +# Routers provide an easy way of automatically determining the URL conf. +router = routers.DefaultRouter(trailing_slash=False) +router.register( + r"jobs/(?P\d+)/connector/(?P\w+)", + ConnectorActionViewSet, + basename="connectorreport", +) +router.register(r"connector", ConnectorConfigViewSet, basename="connector") + +urlpatterns = [ + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/connectors_manager/views.py b/Submodules/IntelOwl/api_app/connectors_manager/views.py new file mode 100644 index 0000000..1ef173c --- /dev/null +++ b/Submodules/IntelOwl/api_app/connectors_manager/views.py @@ -0,0 +1,27 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from ..views import PythonConfigViewSet, PythonReportActionViewSet +from .models import ConnectorReport +from .serializers import ConnectorConfigSerializer + +logger = logging.getLogger(__name__) + + +__all__ = [ + "ConnectorConfigViewSet", + "ConnectorActionViewSet", +] + + +class ConnectorConfigViewSet(PythonConfigViewSet): + serializer_class = ConnectorConfigSerializer + + +class ConnectorActionViewSet(PythonReportActionViewSet): + @classmethod + @property + def report_model(cls): + return ConnectorReport diff --git a/Submodules/IntelOwl/api_app/decorators.py b/Submodules/IntelOwl/api_app/decorators.py new file mode 100644 index 0000000..3b4be8d --- /dev/null +++ b/Submodules/IntelOwl/api_app/decorators.py @@ -0,0 +1,58 @@ +import functools +import logging + +from django.http import HttpResponse + +logger = logging.getLogger(__name__) + + +def deprecated_endpoint(deprecation_date=None, end_of_life_date=None): + """ + Returns a decorator which informs requester that + the decorated endpoint has been deprecated. + """ + + def decorator_deprecated(func): + """Amend the request with information that + the endpoint has been deprecated and when it will be removed + """ + + @functools.wraps(func) + def wrapper_deprecated(*args, **kwargs): + # do something before handling the request, could e.g. issue a django signal + logger.warning("Deprecated endpoint %s called", func.__name__) + + response: HttpResponse = func(*args, **kwargs) + + # amend the response with deprecation information + if isinstance(response, HttpResponse): + response.headers["X-Deprecated"] = "" + if deprecation_date: + response.headers["X-Deprecation-Date"] = deprecation_date + if end_of_life_date: + response.headers["X-End-Of-Life-Date"] = deprecation_date + return response + + return wrapper_deprecated + + return decorator_deprecated + + +def prevent_signal_recursion(func): + @functools.wraps(func) + def no_recursion(sender, instance=None, **kwargs): + if not instance: + return + + if hasattr(instance, "_dirty"): + return + + func(sender, instance=instance, **kwargs) + + try: + instance._dirty = True + instance.save() + finally: + del instance._dirty + + return no_recursion diff --git a/Submodules/IntelOwl/api_app/defaults.py b/Submodules/IntelOwl/api_app/defaults.py new file mode 100644 index 0000000..c26ea9c --- /dev/null +++ b/Submodules/IntelOwl/api_app/defaults.py @@ -0,0 +1,20 @@ +from django.conf import settings +from django.utils import timezone + + +def config_default(): + return dict(queue=settings.DEFAULT_QUEUE, soft_time_limit=60) + + +def default_runtime(): + return { + "analyzers": {}, + "connectors": {}, + "pivots": {}, + "visualizers": {}, + } + + +def file_directory_path(instance, filename): + now = timezone.now().strftime("%Y_%m_%d_%H_%M_%S") + return f"job_{now}_{filename}" diff --git a/Submodules/IntelOwl/api_app/documents.py b/Submodules/IntelOwl/api_app/documents.py new file mode 100644 index 0000000..fc43b7f --- /dev/null +++ b/Submodules/IntelOwl/api_app/documents.py @@ -0,0 +1,73 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django_elasticsearch_dsl import Document, fields +from django_elasticsearch_dsl.registries import registry + +from .models import Job + + +@registry.register_document +class JobDocument(Document): + # Object/List fields + analyzers_to_execute = fields.NestedField( + properties={"name": fields.KeywordField()} + ) + connectors_to_execute = fields.NestedField( + properties={"name": fields.KeywordField()} + ) + visualizers_to_execute = fields.NestedField( + properties={"name": fields.KeywordField()} + ) + playbook_to_execute = fields.KeywordField() + + # Normal fields + errors = fields.TextField() + # Keyword fields to allow aggregations/vizualizations + source = fields.KeywordField() + status = fields.KeywordField() + md5 = fields.KeywordField() + tlp = fields.KeywordField() + observable_name = fields.KeywordField() + observable_classification = fields.KeywordField() + file_name = fields.KeywordField() + file_mimetype = fields.KeywordField() + # Nested (ForeignKey) fields + tags = fields.NestedField( + properties={"label": fields.KeywordField(), "color": fields.TextField()} + ) + analyzerreports = fields.NestedField( + properties={ + "name": fields.KeywordField(), + "status": fields.KeywordField(), + "report": fields.ObjectField(), + "errors": fields.TextField(), + "start_time": fields.DateField(), + "end_time": fields.DateField(), + } + ) + connector_reports = fields.NestedField( + properties={ + "name": fields.KeywordField(), + "status": fields.KeywordField(), + "report": fields.ObjectField(), + "errors": fields.TextField(), + "start_time": fields.DateField(), + "end_time": fields.DateField(), + } + ) + + class Index: + # Name of the Elasticsearch index + name = "jobs" + + class Django: + model = Job # The model associated with this Document + + # The fields of the model you want to be indexed in Elasticsearch + fields = [ + "is_sample", + "received_request_time", + "finished_analysis_time", + "process_time", + ] diff --git a/Submodules/IntelOwl/api_app/enums.py b/Submodules/IntelOwl/api_app/enums.py new file mode 100644 index 0000000..4138d36 --- /dev/null +++ b/Submodules/IntelOwl/api_app/enums.py @@ -0,0 +1,14 @@ +import enum + +from api_app.analyzers_manager.classes import FileAnalyzer, ObservableAnalyzer +from api_app.connectors_manager.classes import Connector +from api_app.ingestors_manager.classes import Ingestor +from api_app.visualizers_manager.classes import Visualizer + + +class PythonClasses(enum.Enum): + OBSERVABLE_ANALYZER = ObservableAnalyzer + FILE_ANALYZER = FileAnalyzer + CONNECTOR = Connector + VISUALIZER = Visualizer + INGESTOR = Ingestor diff --git a/Submodules/IntelOwl/api_app/exceptions.py b/Submodules/IntelOwl/api_app/exceptions.py new file mode 100644 index 0000000..ef7579c --- /dev/null +++ b/Submodules/IntelOwl/api_app/exceptions.py @@ -0,0 +1,20 @@ +import logging + +from rest_framework.exceptions import ValidationError +from rest_framework.request import Request + +from certego_saas.ext.exceptions import custom_exception_handler + +logger = logging.getLogger(__name__) + + +def logging_exception_handler(exc, context): + if isinstance(exc, ValidationError): + request: Request = context["request"] + logger.info( + f"Validation error: {str(exc)} " + f"raised by user:{request.user}" + f" with content:{request.data}" + ) + logger.info(context) + return custom_exception_handler(exc, context) diff --git a/Submodules/IntelOwl/api_app/fields.py b/Submodules/IntelOwl/api_app/fields.py new file mode 100644 index 0000000..2ef6387 --- /dev/null +++ b/Submodules/IntelOwl/api_app/fields.py @@ -0,0 +1,18 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django import forms +from django.contrib.postgres.fields import ArrayField + + +class ChoiceArrayField(ArrayField): + def formfield(self, **kwargs): + defaults = { + "form_class": forms.TypedMultipleChoiceField, + "choices": self.base_field.choices, + "coerce": self.base_field.to_python, + "widget": forms.CheckboxSelectMultiple, + } + defaults.update(kwargs) + # this super call parameter is required + return super(ArrayField, self).formfield(**defaults) diff --git a/Submodules/IntelOwl/api_app/filters.py b/Submodules/IntelOwl/api_app/filters.py new file mode 100644 index 0000000..d65ce05 --- /dev/null +++ b/Submodules/IntelOwl/api_app/filters.py @@ -0,0 +1,66 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import rest_framework_filters as filters +from django.db.models import Q + +from .analyzers_manager.constants import ObservableTypes +from .models import Job + +__all__ = [ + "JobFilter", +] + + +class JobFilter(filters.FilterSet): + is_sample = filters.BooleanFilter() + md5 = filters.CharFilter(lookup_expr="icontains") + observable_name = filters.CharFilter(lookup_expr="icontains") + file_name = filters.CharFilter(lookup_expr="icontains") + file_mimetype = filters.CharFilter(lookup_expr="icontains") + tags = filters.BaseInFilter(field_name="tags__label", lookup_expr="in") + playbook_to_execute = filters.CharFilter( + field_name="playbook_to_execute__name", lookup_expr="icontains" + ) + + # extra + user = filters.CharFilter(method="filter_for_user") + id = filters.CharFilter(method="filter_for_id") + type = filters.CharFilter(method="filter_for_type") + name = filters.CharFilter(method="filter_for_name") + + @staticmethod + def filter_for_user(queryset, value, user, *args, **kwargs): + return queryset.filter(user__username__icontains=user) + + @staticmethod + def filter_for_id(queryset, value, _id, *args, **kwargs): + try: + int_id = int(_id) + except ValueError: + # this is to manage bad data as input + return queryset + else: + return queryset.filter(id=int_id) + + @staticmethod + def filter_for_type(queryset, value, _type, *args, **kwargs): + if _type in ObservableTypes.values: + return queryset.filter(observable_classification=_type) + return queryset.filter(file_mimetype__icontains=_type) + + @staticmethod + def filter_for_name(queryset, value, name, *args, **kwargs): + return queryset.filter( + Q(observable_name__icontains=name) | Q(file_name__icontains=name) + ) + + class Meta: + model = Job + fields = { + "received_request_time": ["lte", "gte"], + "finished_analysis_time": ["lte", "gte"], + "observable_classification": ["exact"], + "tlp": ["exact"], + "status": ["exact"], + } diff --git a/Submodules/IntelOwl/api_app/fixtures/0001_user.json b/Submodules/IntelOwl/api_app/fixtures/0001_user.json new file mode 100644 index 0000000..9bd664d --- /dev/null +++ b/Submodules/IntelOwl/api_app/fixtures/0001_user.json @@ -0,0 +1,37 @@ +[ { + "model": "certego_saas_user.user", + "pk": 1, + "fields": { + "password": "", + "last_login": null, + "is_superuser": true, + "username": "superuser@intelowl.org", + "first_name": "", + "last_name": "", + "email": "", + "is_staff": true, + "is_active": true, + "date_joined": "2023-02-23T08:53:02.830Z", + "groups": [], + "user_permissions": [] + } + }, + { + "model": "certego_saas_user.user", + "pk": 2, + "fields": { + "password": "", + "last_login": null, + "is_superuser": false, + "username": "user", + "first_name": "", + "last_name": "", + "email": "", + "is_staff": false, + "is_active": true, + "date_joined": "2023-02-23T08:53:02.830Z", + "groups": [], + "user_permissions": [] + } + } +] \ No newline at end of file diff --git a/Submodules/IntelOwl/api_app/fixtures/__init__.py b/Submodules/IntelOwl/api_app/fixtures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/forms.py b/Submodules/IntelOwl/api_app/forms.py new file mode 100644 index 0000000..235d709 --- /dev/null +++ b/Submodules/IntelOwl/api_app/forms.py @@ -0,0 +1,106 @@ +from django import forms +from django.core.exceptions import ValidationError + +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.connectors_manager.models import ConnectorConfig +from api_app.models import OrganizationPluginConfiguration, Parameter +from api_app.pivots_manager.models import PivotConfig +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.visualizers_manager.models import VisualizerConfig + + +class MultilineJSONField(forms.JSONField): + @staticmethod + def _cleaning_and_multiline(value): + if value is not None and "\n" in value: + cleaned_value = [] + for line in value.splitlines(): + line.replace("\r", "") + line.replace('"', "") + line = line + "\\n" + cleaned_value.append(line) + value = '"' + "".join(cleaned_value) + '"' + return value + + def to_python(self, value): + return super().to_python(self._cleaning_and_multiline(value)) + + def has_changed(self, initial, data): + return super().has_changed(initial, self._cleaning_and_multiline(data)) + + def bound_data(self, data, initial): + return super().bound_data(self._cleaning_and_multiline(data), initial) + + +class ParameterInlineForm(forms.ModelForm): + default = MultilineJSONField(required=False) + + class Meta: + model = Parameter + fields = [ + "name", + "type", + "description", + "is_secret", + "required", + "python_module", + ] + + +class OrganizationPluginConfigurationForm(forms.ModelForm): + analyzer = forms.ModelChoiceField( + queryset=AnalyzerConfig.objects.filter(orgs_configuration__isnull=True), + required=False, + ) + connector = forms.ModelChoiceField( + queryset=ConnectorConfig.objects.filter(orgs_configuration__isnull=True), + required=False, + ) + + visualizer = forms.ModelChoiceField( + queryset=VisualizerConfig.objects.filter(orgs_configuration__isnull=True), + required=False, + ) + pivot = forms.ModelChoiceField( + queryset=PivotConfig.objects.filter(orgs_configuration__isnull=True), + required=False, + ) + playbook = forms.ModelChoiceField( + queryset=PlaybookConfig.objects.filter(orgs_configuration__isnull=True), + required=False, + ) + _plugins = ["analyzer", "connector", "visualizer", "pivot", "playbook"] + + def validate_unique(self) -> None: + number_plugins = sum( + bool(self.cleaned_data.get(val, False)) for val in self._plugins + ) + if number_plugins != 1 and not self.instance.pk: + self.add_error( + field=None, + error={ + field: "You must select exactly one configuration" + for field in self._plugins + }, + ) + return super().validate_unique() + + def save(self, commit=True): + if not self.instance.pk: + for field in self._plugins: + config = self.cleaned_data.get(field, None) + if config: + break + else: + raise ValidationError("Config is required") + instance = super().save(commit=False) + instance.config = config + else: + instance = super().save(commit=False) + if commit: + instance.save() + return instance + + class Meta: + model = OrganizationPluginConfiguration + fields = ["disabled", "disabled_comment", "organization", "rate_limit_timeout"] diff --git a/Submodules/IntelOwl/api_app/helpers.py b/Submodules/IntelOwl/api_app/helpers.py new file mode 100644 index 0000000..c2ecf5d --- /dev/null +++ b/Submodules/IntelOwl/api_app/helpers.py @@ -0,0 +1,86 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# general helper functions used by the Django API + +import hashlib +import ipaddress +import logging +import random +import re +import warnings + +from django.utils import timezone + +logger = logging.getLogger(__name__) + + +def get_now_str(): + return str(timezone.now()) + + +def get_now(): + return timezone.now() + + +def gen_random_colorhex() -> str: + # flake8: noqa + r = lambda: random.randint(0, 255) + return "#%02X%02X%02X" % (r(), r(), r()) + + +def calculate_md5(value) -> str: + return hashlib.md5(value).hexdigest() # skipcq BAN-B324 + + +def calculate_sha1(value) -> str: + return hashlib.sha1(value).hexdigest() # skipcq BAN-B324 + + +def calculate_sha256(value) -> str: + return hashlib.sha256(value).hexdigest() # skipcq BAN-B324 + + +def get_ip_version(ip_value): + """ + Returns ip version + Supports IPv4 and IPv6 + """ + ip_type = None + try: + ip = ipaddress.ip_address(ip_value) + ip_type = ip.version + except ValueError as e: + logger.error(e) + return ip_type + + +def get_hash_type(hash_value): + """ + Returns hash type + Supports md5, sha1, sha256 and sha512 + """ + RE_HASH_MAP = { + "md5": re.compile(r"^[a-f\d]{32}$", re.IGNORECASE | re.ASCII), + "sha-1": re.compile(r"^[a-f\d]{40}$", re.IGNORECASE | re.ASCII), + "sha-256": re.compile(r"^[a-f\d]{64}$", re.IGNORECASE | re.ASCII), + "sha-512": re.compile(r"^[a-f\d]{128}$", re.IGNORECASE | re.ASCII), + } + + detected_hash_type = None + for hash_type, re_hash in RE_HASH_MAP.items(): + if re.match(re_hash, hash_value): + detected_hash_type = hash_type + break + return detected_hash_type # stays None if no matches + + +def deprecated(message: str): + def decorator(func): + def wrapper(*args, **kwargs): + warnings.warn(message, DeprecationWarning, stacklevel=2) + return func(*args, **kwargs) + + return wrapper + + return decorator diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/__init__.py b/Submodules/IntelOwl/api_app/ingestors_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/admin.py b/Submodules/IntelOwl/api_app/ingestors_manager/admin.py new file mode 100644 index 0000000..3fdb51c --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/admin.py @@ -0,0 +1,24 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.contrib import admin + +from api_app.admin import AbstractReportAdminView, PythonConfigAdminView +from api_app.ingestors_manager.models import IngestorConfig, IngestorReport + + +@admin.register(IngestorReport) +class IngestorReportAdminView(AbstractReportAdminView): + ... + + +@admin.register(IngestorConfig) +class IngestorConfigAdminView(PythonConfigAdminView): + list_display = ( + "name", + "python_module", + "disabled", + "playbook_to_execute", + "schedule", + ) + exclude = ["user", "periodic_task"] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/apps.py b/Submodules/IntelOwl/api_app/ingestors_manager/apps.py new file mode 100644 index 0000000..bfb01e9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class IngestorsManagerConfig(AppConfig): + name = "api_app.ingestors_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/classes.py b/Submodules/IntelOwl/api_app/ingestors_manager/classes.py new file mode 100644 index 0000000..43251a4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/classes.py @@ -0,0 +1,77 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import abc +import logging +import typing +from collections import deque +from typing import Any, Type + +from django.utils.functional import cached_property + +from ..choices import TLP, PythonModuleBasePaths +from ..classes import Plugin +from .exceptions import IngestorConfigurationException, IngestorRunException +from .models import IngestorConfig, IngestorReport + +logger = logging.getLogger(__name__) + + +class Ingestor(Plugin, metaclass=abc.ABCMeta): + def __init__(self, config: IngestorConfig, **kwargs): + super().__init__(config, **kwargs) + + @classmethod + @property + def python_base_path(cls): + return PythonModuleBasePaths.Ingestor.value + + @abc.abstractmethod + def run(self) -> typing.Iterator[Any]: + raise NotImplementedError() + + @classmethod + @property + def report_model(cls) -> Type[IngestorReport]: + return IngestorReport + + @classmethod + @property + def config_model(cls) -> Type[IngestorConfig]: + return IngestorConfig + + def get_exceptions_to_catch(self) -> list: + return [ + IngestorConfigurationException, + IngestorRunException, + ] + + @cached_property + def _user(self): + self._config: IngestorConfig + return self._config.user + + def before_run(self): + self._config: IngestorConfig + self._config.validate_playbook_to_execute(self._user) + + def after_run_success(self, content): + super().after_run_success(content) + self._config: IngestorConfig + # exhaust generator + deque( + self._config.create_jobs( + # every job created from an ingestor + content, + TLP.CLEAR.value, + self._user, + ), + maxlen=0, + ) + + def execute_pivots(self) -> None: + # we do not have a job, meaning that we have no pivots + return + + @cached_property + def _job(self) -> None: + return None diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/exceptions.py b/Submodules/IntelOwl/api_app/ingestors_manager/exceptions.py new file mode 100644 index 0000000..7323c18 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/exceptions.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +class NotRunnableIngestor(Exception): + pass + + +class IngestorConfigurationException(Exception): + pass + + +class IngestorRunException(Exception): + pass diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/ingestors/__init__.py b/Submodules/IntelOwl/api_app/ingestors_manager/ingestors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/ingestors/threatfox.py b/Submodules/IntelOwl/api_app/ingestors_manager/ingestors/threatfox.py new file mode 100644 index 0000000..57039b7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/ingestors/threatfox.py @@ -0,0 +1,80 @@ +import logging +from typing import Any, Iterable +from unittest.mock import patch + +import requests + +from api_app.ingestors_manager.classes import Ingestor +from api_app.ingestors_manager.exceptions import IngestorRunException +from tests.mock_utils import MockUpResponse, if_mock_connections + +logger = logging.getLogger(__name__) + + +class ThreatFox(Ingestor): + days: int + + BASE_URL = "https://threatfox-api.abuse.ch/api/v1/" + + def run(self) -> Iterable[Any]: + result = requests.post( + self.BASE_URL, json={"query": "get_iocs", "days": self.days} + ) + result.raise_for_status() + content = result.json() + logger.info(f"Threatfox data is {content}") + if content["query_status"] != "ok": + raise IngestorRunException( + f"Query status is invalid: {content['query_status']}" + ) + if not isinstance(content["data"], list): + raise IngestorRunException(f"Content {content} not expected") + for elem in content["data"]: + if elem["ioc_type"] == "ip:port": + # we do not manage ip with the port at the moment + yield elem["ioc"].split(":")[0] + else: + yield elem["ioc"] + + @classmethod + def _monkeypatch(cls): + patches = [ + if_mock_connections( + patch( + "requests.post", + return_value=MockUpResponse( + { + "query_status": "ok", + "data": [ + { + "id": "41", + "ioc": "gaga.com", + "threat_type": "botnet_cc", + "threat_type_desc": "Indicator that" + " identifies a botnet" + " command&control server (C&C)", + "ioc_type": "domain", + "ioc_type_desc": "Domain that is used for" + " botnet Command&control (C&C)", + "malware": "win.dridex", + "malware_printable": "Dridex", + "malware_alias": None, + "malware_malpedia": r"https://malpedia.caad." + "fkie.fraunhofer.de" + r"/details/win.dridex", + "confidence_level": 50, + "first_seen": "2020-12-08 13:36:27 UTC", + "last_seen": None, + "reporter": "abuse_ch", + "reference": r"https://twitter.com/JAMESWT_MHT" + r"/status/1336229725082177536", + "tags": ["exe", "test"], + } + ], + }, + 200, + ), + ), + ) + ] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0001_initial_squashed.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0001_initial_squashed.py new file mode 100644 index 0000000..07cdbba --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0001_initial_squashed.py @@ -0,0 +1,208 @@ +# Generated by Django 4.2.8 on 2024-02-08 11:11 + +import django.contrib.postgres.fields +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + +import api_app.interfaces + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("api_app", "0001_1_initial_squashed"), + ("playbooks_manager", "0001_initial_squashed"), + ] + + operations = [ + migrations.CreateModel( + name="IngestorConfig", + fields=[ + ( + "name", + models.CharField( + max_length=100, + primary_key=True, + serialize=False, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", + "Your name should " + "match the [" + "A-Za-z0-9_] " + "characters", + ) + ], + ), + ), + ( + "python_module", + models.ForeignKey( + limit_choices_to={ + "base_path": "api_app.ingestors_manager.ingestors" + }, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)ss", + to="api_app.pythonmodule", + ), + ), + ("description", models.TextField()), + ("disabled", models.BooleanField(default=False)), + ( + "health_check_status", + models.BooleanField(default=True, editable=False), + ), + ( + "health_check_task", + models.OneToOneField( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="healthcheck_for_%(class)s", + to="django_celery_beat.periodictask", + ), + ), + ( + "soft_time_limit", + models.IntegerField( + default=60, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ("routing_key", models.CharField(default="default", max_length=50)), + ( + "periodic_task", + models.OneToOneField( + on_delete=django.db.models.deletion.PROTECT, + related_name="ingestor", + to="django_celery_beat.periodictask", + ), + ), + ( + "playbook_to_execute", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ingestors", + to="playbooks_manager.playbookconfig", + ), + ), + ( + "schedule", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="ingestors", + to="django_celery_beat.crontabschedule", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ingestors", + to=settings.AUTH_USER_MODEL, + ), + ), + ("maximum_jobs", models.IntegerField(default=10)), + ], + options={"abstract": False, "ordering": ["name", "disabled"]}, + bases=(models.Model, api_app.interfaces.CreateJobsFromPlaybookInterface), + ), + migrations.AddIndex( + model_name="ingestorconfig", + index=models.Index( + fields=["python_module", "disabled"], + name="ingestors_m_python__5c8ce0_idx", + ), + ), + migrations.RenameIndex( + model_name="ingestorconfig", + new_name="ingestors_m_python__b7a859_idx", + old_name="ingestors_m_python__5c8ce0_idx", + ), + migrations.CreateModel( + name="IngestorReport", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(blank=True, default="", max_length=50)), + ( + "status", + models.CharField( + choices=[ + ("FAILED", "Failed"), + ("PENDING", "Pending"), + ("RUNNING", "Running"), + ("SUCCESS", "Success"), + ("KILLED", "Killed"), + ], + max_length=50, + ), + ), + ("report", models.JSONField(default=list, validators=[])), + ( + "errors", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=512), + blank=True, + default=list, + size=None, + ), + ), + ("start_time", models.DateTimeField(default=django.utils.timezone.now)), + ("end_time", models.DateTimeField(default=django.utils.timezone.now)), + ("task_id", models.UUIDField(null=True, blank=True)), + ( + "config", + models.ForeignKey( + "IngestorConfig", + related_name="reports", + on_delete=models.CASCADE, + ), + ), + ( + "max_size_report", + models.IntegerField(default=None, null=True, blank=True), + ), + ( + "job", + models.ForeignKey( + "api_app.Job", + related_name="%(class)ss", + on_delete=models.SET_NULL, + null=True, + blank=True, + ), + ), + ("sent_to_bi", models.BooleanField(default=False, editable=False)), + ("parameters", models.JSONField(default={}, editable=False)), + ], + ), + migrations.AddIndex( + model_name="ingestorreport", + index=models.Index( + fields=["sent_to_bi", "-start_time"], name="ingestorreportsBISearch" + ), + ), + migrations.AlterModelOptions( + name="ingestorreport", + options={"ordering": ["pk"]}, + ), + migrations.AlterField( + model_name="ingestorreport", + name="parameters", + field=models.JSONField(editable=False), + ), + ] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0002_0000_ingestor_config_threatfox.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0002_0000_ingestor_config_threatfox.py new file mode 100644 index 0000000..345dadf --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0002_0000_ingestor_config_threatfox.py @@ -0,0 +1,182 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "ThreatFox", + "python_module": { + "health_check_schedule": None, + "update_schedule": { + "minute": "30", + "hour": "7", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "update_task": { + "crontab": { + "minute": "30", + "hour": "7", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "ThreatFoxIngestor", + "task": "intel_owl.tasks.execute_ingestor", + "kwargs": '{"config_pk": "ThreatFox"}', + "queue": "default", + "enabled": False, + }, + "module": "threatfox.ThreatFox", + "base_path": "api_app.ingestors_manager.ingestors", + }, + "schedule": { + "minute": "30", + "hour": "7", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "periodic_task": { + "crontab": { + "minute": "30", + "hour": "7", + "day_of_week": "*", + "day_of_month": "*", + "month_of_year": "*", + }, + "name": "ThreatFoxIngestor", + "task": "intel_owl.tasks.execute_ingestor", + "kwargs": '{"config_pk": "ThreatFox"}', + "queue": "default", + "enabled": False, + }, + "user": { + "username": "ThreatFoxIngestor", + "first_name": "", + "last_name": "", + "email": "", + }, + "description": "Threatfox ingestor", + "disabled": True, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "maximum_jobs": 10, + "health_check_task": None, + "playbook_to_execute": "Popular_IP_Reputation_Services", + "model": "ingestors_manager.IngestorConfig", +} + +params = [ + { + "python_module": { + "module": "threatfox.ThreatFox", + "base_path": "api_app.ingestors_manager.ingestors", + }, + "name": "days", + "type": "int", + "description": "Days to check. From 1 to 7", + "is_secret": False, + "required": True, + } +] + +values = [ + { + "parameter": { + "python_module": { + "module": "threatfox.ThreatFox", + "base_path": "api_app.ingestors_manager.ingestors", + }, + "name": "days", + "type": "int", + "description": "Days to check. From 1 to 7", + "is_secret": False, + "required": True, + }, + "for_organization": False, + "value": 1, + "updated_at": "2024-02-09T10:52:22.088107Z", + "owner": None, + "analyzer_config": None, + "connector_config": None, + "visualizer_config": None, + "ingestor_config": "ThreatFox", + "pivot_config": None, + } +] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("ingestors_manager", "0001_initial_squashed"), + ("playbooks_manager", "0002_0004_playbook_config_sample_static_analysis"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_1_change_primary_key.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_1_change_primary_key.py new file mode 100644 index 0000000..082b63b --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_1_change_primary_key.py @@ -0,0 +1,12 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("ingestors_manager", "0002_0000_ingestor_config_threatfox"), + ("playbooks_manager", "0001_initial_squashed"), + ("api_app", "0001_2_initial_squashed"), + ] + + operations = [] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_2_change_primary_key.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_2_change_primary_key.py new file mode 100644 index 0000000..9adc19e --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_2_change_primary_key.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("ingestors_manager", "0016_1_change_primary_key"), + ] + + operations = [ + migrations.AlterField( + model_name="ingestorreport", + name="config", + field=models.CharField(max_length=100, null=False, blank=False), + ), + ] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_3_change_primary_key.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_3_change_primary_key.py new file mode 100644 index 0000000..b65712b --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_3_change_primary_key.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("ingestors_manager", "0016_2_change_primary_key"), + ("api_app", "0057_2_change_primary_key"), + ] + + operations = [ + migrations.RunSQL( + 'ALTER TABLE "ingestors_manager_ingestorconfig" DROP CONSTRAINT "ingestors_manager_ingestorconfig_pkey" CASCADE;' + ), + migrations.AlterField( + model_name="ingestorconfig", + name="name", + field=models.CharField( + max_length=100, + unique=True, + primary_key=False, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", "Your name should match the [A-Za-z0-9_] characters" + ) + ], + ), + ), + migrations.AddField( + model_name="ingestorconfig", + name="id", + field=models.BigAutoField( + auto_created=True, serialize=False, verbose_name="ID", primary_key=True + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_4_change_primary_key.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_4_change_primary_key.py new file mode 100644 index 0000000..ffb21b8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0016_4_change_primary_key.py @@ -0,0 +1,47 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + IngestorReport = apps.get_model("ingestors_manager", "IngestorReport") + IngestorConfig = apps.get_model("ingestors_manager", "IngestorConfig") + name = IngestorConfig.objects.filter( + name=models.OuterRef("old_config") + ).values_list("pk")[:1] + IngestorReport.objects.update(config=models.Subquery(name)) + + +class Migration(migrations.Migration): + dependencies = [ + ("ingestors_manager", "0016_3_change_primary_key"), + ] + + operations = [ + migrations.RenameField( + model_name="ingestorreport", old_name="config", new_name="old_config" + ), + migrations.AddField( + model_name="ingestorreport", + name="config", + field=models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="ingestors_manager.ingestorconfig", + null=True, + ), + preserve_default=False, + ), + migrations.RunPython(migrate), + migrations.AlterField( + model_name="ingestorreport", + name="config", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="ingestors_manager.ingestorconfig", + ), + ), + migrations.RemoveField(model_name="ingestorreport", name="old_config"), + ] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0017_2_change_primary_key.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0017_2_change_primary_key.py new file mode 100644 index 0000000..4677d3b --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0017_2_change_primary_key.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_1_change_primary_key"), + ("ingestors_manager", "0016_4_change_primary_key"), + ] + + operations = [ + migrations.AlterField( + model_name="ingestorconfig", + name="playbook_to_execute", + field=models.CharField(max_length=100, null=True, blank=True), + ), + ] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0017_4_change_primary_key.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0017_4_change_primary_key.py new file mode 100644 index 0000000..decd462 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/0017_4_change_primary_key.py @@ -0,0 +1,58 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + IngestorConfig = apps.get_model("ingestors_manager", "IngestorConfig") + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + IngestorConfig.objects.update( + playbook_to_execute=models.Subquery( + PlaybookConfig.objects.filter( + name=models.OuterRef("old_playbook_to_execute") + ).values_list("pk")[:1] + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_3_change_primary_key"), + ("ingestors_manager", "0017_2_change_primary_key"), + ] + + operations = [ + migrations.RenameField( + model_name="ingestorconfig", + old_name="playbook_to_execute", + new_name="old_playbook_to_execute", + ), + migrations.AddField( + model_name="ingestorconfig", + name="playbook_to_execute", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="ingestors", + to="playbooks_manager.playbookconfig", + ), + preserve_default=False, + ), + migrations.RunPython( + migrate, + ), + migrations.AlterField( + model_name="ingestorconfig", + name="playbook_to_execute", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="ingestors", + to="playbooks_manager.playbookconfig", + ), + ), + migrations.RemoveField( + model_name="ingestorconfig", name="old_playbook_to_execute" + ), + ] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/ingestors_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/models.py b/Submodules/IntelOwl/api_app/ingestors_manager/models.py new file mode 100644 index 0000000..3f55a2d --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/models.py @@ -0,0 +1,126 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging + +from django.conf import settings +from django.db import models +from django.db.models import QuerySet +from django_celery_beat.models import CrontabSchedule, PeriodicTask + +from api_app.choices import PythonModuleBasePaths +from api_app.ingestors_manager.exceptions import IngestorConfigurationException +from api_app.ingestors_manager.queryset import IngestorReportQuerySet +from api_app.interfaces import CreateJobsFromPlaybookInterface +from api_app.models import ( + AbstractReport, + Job, + OrganizationPluginConfiguration, + PythonConfig, + PythonModule, +) +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.queryset import IngestorQuerySet + +logger = logging.getLogger(__name__) + + +class IngestorReport(AbstractReport): + objects = IngestorReportQuerySet.as_manager() + config = models.ForeignKey( + "IngestorConfig", related_name="reports", on_delete=models.CASCADE + ) + report = models.JSONField(default=list, validators=[]) + name = models.CharField(blank=True, default="", max_length=50) + task_id = models.UUIDField(null=True, blank=True) + job = models.ForeignKey( + "api_app.Job", + related_name="%(class)ss", + on_delete=models.SET_NULL, + null=True, + blank=True, + ) + max_size_report = models.IntegerField(default=None, null=True, blank=True) + + class Meta: + ordering = ["pk"] + indexes = AbstractReport.Meta.indexes + + def clean_report(self): + if isinstance(self.report, list) and self.max_size_report is not None: + len_report = len(self.report) + if len_report > self.max_size_report: + logger.warning( + f"Report {self.pk} has {len_report} " + f"while max_size is {self.max_size_report}" + ) + self.report = self.report[: self.max_size_report] + + def clean(self): + super().clean() + self.clean_report() + + +class IngestorConfig(PythonConfig, CreateJobsFromPlaybookInterface): + objects = IngestorQuerySet.as_manager() + python_module = models.ForeignKey( + PythonModule, + on_delete=models.PROTECT, + related_name="%(class)ss", + limit_choices_to={"base_path": PythonModuleBasePaths.Ingestor.value}, + ) + playbook_to_execute = models.ForeignKey( + PlaybookConfig, + related_name="ingestors", + on_delete=models.CASCADE, + ) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="ingestors", + ) + schedule = models.ForeignKey( + CrontabSchedule, related_name="ingestors", on_delete=models.PROTECT + ) + periodic_task = models.OneToOneField( + PeriodicTask, related_name="ingestor", on_delete=models.PROTECT + ) + maximum_jobs = models.IntegerField(default=10) + + org_configuration = None + + @property + def disabled_in_organizations(self) -> QuerySet: + return OrganizationPluginConfiguration.objects.none() + + @classmethod + @property + def plugin_type(cls) -> str: + return "4" + + @classmethod + @property + def config_exception(cls): + return IngestorConfigurationException + + @classmethod + @property + def serializer_class(cls): + from api_app.ingestors_manager.serializers import IngestorConfigSerializer + + return IngestorConfigSerializer + + def generate_empty_report(self, job: Job, task_id: str, status: str): + # every time we execute the ingestor we have to create a new report + # instead of using the update/create + # because we do not have the same unique constraints + return self.python_module.python_class.report_model.objects.create( + job=job, + config=self, + status=status, + task_id=task_id, + max_size_report=self.maximum_jobs, + parameters=self._get_params(self.user, {}), + ) + + def get_or_create_org_configuration(self): + return None diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/queryset.py b/Submodules/IntelOwl/api_app/ingestors_manager/queryset.py new file mode 100644 index 0000000..5b1d2c3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/queryset.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING, Type + +from api_app.queryset import AbstractReportQuerySet + +if TYPE_CHECKING: + from api_app.ingestors_manager.serializers import IngestorReportBISerializer + + +class IngestorReportQuerySet(AbstractReportQuerySet): + @classmethod + def _get_bi_serializer_class(cls) -> Type["IngestorReportBISerializer"]: + from api_app.ingestors_manager.serializers import IngestorReportBISerializer + + return IngestorReportBISerializer diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/serializers.py b/Submodules/IntelOwl/api_app/ingestors_manager/serializers.py new file mode 100644 index 0000000..76c85a1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/serializers.py @@ -0,0 +1,71 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from rest_framework import serializers as rfs + +from certego_saas.apps.user.serializers import UserSerializer + +from ..playbooks_manager.models import PlaybookConfig +from ..serializers.celery import CrontabScheduleSerializer, PeriodicTaskSerializer +from ..serializers.plugin import ( + PythonConfigSerializer, + PythonConfigSerializerForMigration, +) +from ..serializers.report import AbstractReportBISerializer, AbstractReportSerializer +from .models import IngestorConfig, IngestorReport + + +class IngestorConfigSerializer(PythonConfigSerializer): + schedule = CrontabScheduleSerializer(read_only=True) + playbook_to_execute = rfs.SlugRelatedField( + queryset=PlaybookConfig.objects.all(), slug_field="name", many=False + ) + + class Meta: + model = IngestorConfig + exclude = ["user", "periodic_task"] + list_serializer_class = PythonConfigSerializer.Meta.list_serializer_class + + def to_internal_value(self, data): + raise NotImplementedError() + + +class IngestorConfigSerializerForMigration(PythonConfigSerializerForMigration): + schedule = CrontabScheduleSerializer(read_only=True) + periodic_task = PeriodicTaskSerializer(read_only=True) + user = UserSerializer(read_only=True, omit=["full_name"]) + + class Meta: + model = IngestorConfig + exclude = [] + + def to_internal_value(self, data): + raise NotImplementedError() + + +class IngestorReportSerializer(AbstractReportSerializer): + name = rfs.SerializerMethodField() + + class Meta: + model = IngestorReport + fields = AbstractReportSerializer.Meta.fields + list_serializer_class = AbstractReportSerializer.Meta.list_serializer_class + + @classmethod + def get_name(cls, instance: IngestorReport): + return instance.name or instance.config.pk + + def to_internal_value(self, data): + raise NotImplementedError() + + +class IngestorReportBISerializer(AbstractReportBISerializer): + name = rfs.SerializerMethodField() + + class Meta: + model = IngestorReport + fields = AbstractReportBISerializer.Meta.fields + list_serializer_class = AbstractReportBISerializer.Meta.list_serializer_class + + @classmethod + def get_name(cls, instance: IngestorReport): + return instance.name or instance.config.pk diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/signals.py b/Submodules/IntelOwl/api_app/ingestors_manager/signals.py new file mode 100644 index 0000000..69b9a43 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/signals.py @@ -0,0 +1,67 @@ +import json +import logging +import uuid + +from django.conf import settings +from django.db.models.signals import post_delete, pre_save +from django.dispatch import receiver +from django_celery_beat.models import PeriodicTask + +from api_app.ingestors_manager.models import IngestorConfig +from api_app.signals import migrate_finished +from certego_saas.apps.user.models import User +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +@receiver(pre_save, sender=IngestorConfig) +def pre_save_ingestor_config(sender, instance: IngestorConfig, *args, **kwargs): + from intel_owl.tasks import execute_ingestor + + user = User.objects.get_or_create(username=f"{instance.name.title()}Ingestor")[0] + user.profile.task_priority = 7 + user.profile.is_robot = True + user.profile.save() + instance.user = user + + periodic_task = PeriodicTask.objects.update_or_create( + name=f"{instance.name.title()}Ingestor", + task=f"{execute_ingestor.__module__}.{execute_ingestor.__name__}", + defaults={ + "crontab": instance.schedule, + "queue": instance.queue, + "kwargs": json.dumps({"config_name": instance.name}), + "enabled": not instance.disabled, + }, + )[0] + instance.periodic_task = periodic_task + return instance + + +@receiver(post_delete, sender=IngestorConfig) +def post_delete_ingestor_config( + sender, instance: IngestorConfig, using, origin, *args, **kwargs +): + instance.periodic_task.delete() + instance.user.delete() + + +@receiver(migrate_finished) +def post_migrate_ingestors_manager( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from intel_owl.tasks import refresh_cache + + refresh_cache.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=3, + args=[IngestorConfig.python_path], + ) diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/urls.py b/Submodules/IntelOwl/api_app/ingestors_manager/urls.py new file mode 100644 index 0000000..07bdc17 --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/urls.py @@ -0,0 +1,16 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +# Routers provide an easy way of automatically determining the URL conf. +from api_app.ingestors_manager.views import IngestorConfigViewSet + +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"ingestor", IngestorConfigViewSet, basename="ingestor") + +urlpatterns = [ + # Viewsets + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/ingestors_manager/views.py b/Submodules/IntelOwl/api_app/ingestors_manager/views.py new file mode 100644 index 0000000..5203d5f --- /dev/null +++ b/Submodules/IntelOwl/api_app/ingestors_manager/views.py @@ -0,0 +1,22 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from rest_framework import status +from rest_framework.response import Response + +from api_app.ingestors_manager.serializers import IngestorConfigSerializer +from api_app.views import PythonConfigViewSet + +logger = logging.getLogger(__name__) + + +class IngestorConfigViewSet(PythonConfigViewSet): + serializer_class = IngestorConfigSerializer + + def disable_in_org(self, request, pk=None): + return Response(status=status.HTTP_404_NOT_FOUND) + + def enable_in_org(self, request, pk=None): + return Response(status=status.HTTP_404_NOT_FOUND) diff --git a/Submodules/IntelOwl/api_app/interfaces.py b/Submodules/IntelOwl/api_app/interfaces.py new file mode 100644 index 0000000..42fcfa4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/interfaces.py @@ -0,0 +1,142 @@ +import io +import logging +from typing import TYPE_CHECKING, Any, Generator, Iterable, Optional, Union + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.functional import cached_property + +from certego_saas.apps.organization.organization import Organization + +if TYPE_CHECKING: + from api_app.playbooks_manager.models import PlaybookConfig + from api_app.models import Job + +from django.core.files import File +from django.http import QueryDict + +from certego_saas.apps.user.models import User + +logger = logging.getLogger(__name__) + + +class CreateJobsFromPlaybookInterface: + playbook_to_execute: "PlaybookConfig" + playbook_to_execute_id: str + name: str + + def validate_playbook_to_execute(self, user: User): + from api_app.playbooks_manager.models import PlaybookConfig + + if ( + not PlaybookConfig.objects.filter(pk=self.playbook_to_execute_id) + .visible_for_user(user) + .exists() + ): + raise RuntimeError( + f"User {user.username} do not have visibility to" + f" playbook {self.playbook_to_execute_id}" + ) + + def _get_serializer(self, value: Any, tlp: str, user: User): + values = value if isinstance(value, (list, Generator)) else [value] + if self.playbook_to_execute.is_sample(): + return self._get_file_serializer(values, tlp, user) + else: + return self._get_observable_serializer(values, tlp, user) + + def _get_observable_serializer(self, values: Iterable[Any], tlp: str, user: User): + from api_app.serializers.job import ObservableAnalysisSerializer + from tests.mock_utils import MockUpRequest + + return ObservableAnalysisSerializer( + data={ + "playbook_requested": self.playbook_to_execute.name, + "observables": [(None, value) for value in values], + "tlp": tlp, + }, + context={"request": MockUpRequest(user=user)}, + many=True, + ) + + def _get_file_serializer( + self, values: Iterable[Union[bytes, File]], tlp: str, user: User + ): + from api_app.serializers.job import FileJobSerializer + from tests.mock_utils import MockUpRequest + + files = [ + data + if isinstance(data, File) + else File(io.BytesIO(data), name=f"{self.name}.{i}") + for i, data in enumerate(values) + ] + query_dict = QueryDict(mutable=True) + data = { + "playbook_requested": self.playbook_to_execute.name, + "tlp": tlp, + } + query_dict.update(data) + query_dict.setlist("files", files) + return FileJobSerializer( + data=query_dict, + context={"request": MockUpRequest(user=user)}, + many=True, + ) + + def create_jobs( + self, + value: Any, + tlp: str, + user: User, + send_task: bool = True, + parent_job=None, + ) -> Generator["Job", None, None]: + try: + serializer = self._get_serializer(value, tlp, user) + except ValueError as e: + logger.exception(e) + raise + else: + serializer.is_valid(raise_exception=True) + yield from serializer.save(send_task=send_task, parent=parent_job) + + +class OwnershipAbstractModel(models.Model): + for_organization = models.BooleanField(default=False) + owner = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="+", + null=True, + blank=True, + ) + + class Meta: + indexes = [ + models.Index( + fields=[ + "owner", + "for_organization", + ] + ) + ] + abstract = True + + def clean_for_organization(self): + if self.for_organization and not self.owner: + raise ValidationError( + "You can't set `for_organization` and not have an owner" + ) + if self.for_organization and not self.owner.has_membership(): + raise ValidationError( + f"You can't create `for_organization` {self.__class__.__name__}" + " if you do not have an organization" + ) + + @cached_property + def organization(self) -> Optional[Organization]: + if self.for_organization: + return self.owner.membership.organization + return None diff --git a/Submodules/IntelOwl/api_app/investigations_manager/__init__.py b/Submodules/IntelOwl/api_app/investigations_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/investigations_manager/admin.py b/Submodules/IntelOwl/api_app/investigations_manager/admin.py new file mode 100644 index 0000000..6f0a385 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/admin.py @@ -0,0 +1,27 @@ +from django.contrib import admin + +from api_app.admin import CustomAdminView +from api_app.investigations_manager.models import Investigation + + +@admin.register(Investigation) +class InvestigationAdminView(CustomAdminView): + list_display = ["name", "start_time", "status", "owner", "get_jobs", "total_jobs"] + list_filter = ["owner", "status"] + search_fields = ["name"] + + @admin.display(description="Total Jobs") + def total_jobs(self, instance: Investigation): + from api_app.models import Job + + string = "" + for i, job in enumerate(instance.jobs.all()): + job: Job + tree = job.get_tree(job) + jobs_repr = " ".join(map(str, tree.values_list("pk", flat=True))) + string += f"Branch {i+1}: jobs -> {jobs_repr}; " + return string + + @admin.display(description="Jobs at first level") + def get_jobs(self, instance: Investigation): + return list(instance.jobs.all().values_list("pk", flat=True)) diff --git a/Submodules/IntelOwl/api_app/investigations_manager/apps.py b/Submodules/IntelOwl/api_app/investigations_manager/apps.py new file mode 100644 index 0000000..6536704 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class InvestigationManagerConfig(AppConfig): + name = "api_app.investigations_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/investigations_manager/choices.py b/Submodules/IntelOwl/api_app/investigations_manager/choices.py new file mode 100644 index 0000000..51786cd --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/choices.py @@ -0,0 +1,7 @@ +from django.db import models + + +class InvestigationStatusChoices(models.TextChoices): + CREATED = "created" + RUNNING = "running" + CONCLUDED = "concluded" diff --git a/Submodules/IntelOwl/api_app/investigations_manager/filters.py b/Submodules/IntelOwl/api_app/investigations_manager/filters.py new file mode 100644 index 0000000..fb32095 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/filters.py @@ -0,0 +1,13 @@ +import rest_framework_filters as filters + +from api_app.investigations_manager.models import Investigation + + +class InvestigationFilter(filters.FilterSet): + class Meta: + model = Investigation + fields = { + "start_time": ["lte", "gte"], + "end_time": ["lte", "gte"], + "status": ["exact"], + } diff --git a/Submodules/IntelOwl/api_app/investigations_manager/migrations/0001_initial.py b/Submodules/IntelOwl/api_app/investigations_manager/migrations/0001_initial.py new file mode 100644 index 0000000..4149255 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/migrations/0001_initial.py @@ -0,0 +1,64 @@ +# Generated by Django 4.2.8 on 2024-02-01 14:27 + +import datetime + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Investigation", + options={"verbose_name_plural": "investigations"}, + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("for_organization", models.BooleanField(default=False)), + ("name", models.CharField(max_length=100)), + ("description", models.TextField(blank=True, default="")), + ("start_time", models.DateTimeField(default=datetime.datetime.now)), + ("end_time", models.DateTimeField(blank=True, default=None, null=True)), + ( + "status", + models.CharField( + choices=[ + ("created", "Created"), + ("running", "Running"), + ("concluded", "Concluded"), + ], + default="created", + max_length=20, + ), + ), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="investigations", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + migrations.AddIndex( + model_name="investigation", + index=models.Index( + fields=["start_time"], name="investigati_start_t_8c993d_idx" + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/investigations_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/investigations_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/investigations_manager/models.py b/Submodules/IntelOwl/api_app/investigations_manager/models.py new file mode 100644 index 0000000..05eec38 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/models.py @@ -0,0 +1,107 @@ +from datetime import datetime +from typing import List + +from django.conf import settings +from django.db import models + +from api_app.choices import TLP +from api_app.interfaces import OwnershipAbstractModel +from api_app.investigations_manager.choices import InvestigationStatusChoices +from api_app.investigations_manager.queryset import InvestigationQuerySet +from api_app.models import ListCachable +from certego_saas.apps.user.models import User + + +class Investigation(OwnershipAbstractModel, ListCachable): + name = models.CharField(max_length=100) + description = models.TextField(default="", blank=True) + + start_time = models.DateTimeField(default=datetime.now) + end_time = models.DateTimeField(default=None, null=True, blank=True) + owner = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="investigations", + ) + status = models.CharField( + choices=InvestigationStatusChoices.choices, + max_length=20, + default=InvestigationStatusChoices.CREATED.value, + ) + Status = InvestigationStatusChoices + + objects = InvestigationQuerySet.as_manager() + + class Meta: + verbose_name_plural = "investigations" + indexes = [models.Index(fields=["start_time"])] + + def __str__(self): + return ( + f"{self.name}:" + f" jobs {', '.join([str(job.pk) for job in self.jobs.all()])} " + f"-> {self.status}" + ) + + def user_can_edit(self, user: User) -> bool: + if ( + # same organization if investigation is at org level + self.for_organization + and ( + user.has_membership() + and self.owner.has_membership() + and user.membership.organization == self.owner.membership.organization + ) + # or same user + ) or user == self.owner: + return True + return False + + def set_correct_status(self, save: bool = True): + from api_app.models import Job + + # if I have some jobs + if self.jobs.exists(): + # and at least one is running + for job in self.jobs.all(): + job: Job + jobs = job.get_tree(job) + if jobs.exclude(status__in=Job.Status.final_statuses()).count() > 0: + self.status = self.Status.RUNNING.value + self.end_time = None + break + # and they are all completed + else: + self.status = self.Status.CONCLUDED.value + self.end_time = ( + self.jobs.order_by("-finished_analysis_time") + .first() + .finished_analysis_time + ) + else: + self.status = self.Status.CREATED.value + self.end_time = None + if save: + self.save(update_fields=["status", "end_time"]) + + @property + def tags(self) -> List[str]: + return list(set(self.jobs.values_list("tags__label", flat=True))) + + @property + def tlp(self) -> TLP: + return ( + max( + TLP[tlp_string] + for tlp_string in self.jobs.values_list("tlp", flat=True) + ) + if self.jobs.exists() + else TLP.CLEAR.value + ) + + @property + def total_jobs(self) -> int: + return ( + sum(job.get_descendant_count() for job in self.jobs.all()) + + self.jobs.count() + ) diff --git a/Submodules/IntelOwl/api_app/investigations_manager/queryset.py b/Submodules/IntelOwl/api_app/investigations_manager/queryset.py new file mode 100644 index 0000000..b5b187b --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/queryset.py @@ -0,0 +1,5 @@ +from api_app.queryset import CleanOnCreateQuerySet, ModelWithOwnershipQuerySet + + +class InvestigationQuerySet(CleanOnCreateQuerySet, ModelWithOwnershipQuerySet): + ... diff --git a/Submodules/IntelOwl/api_app/investigations_manager/serializers.py b/Submodules/IntelOwl/api_app/investigations_manager/serializers.py new file mode 100644 index 0000000..6eada03 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/serializers.py @@ -0,0 +1,30 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from rest_framework import serializers as rfs + +from api_app.investigations_manager.models import Investigation +from api_app.serializers import ModelWithOwnershipSerializer +from api_app.serializers.job import JobTreeSerializer + + +class InvestigationSerializer(ModelWithOwnershipSerializer, rfs.ModelSerializer): + tags = rfs.ListField( + child=rfs.CharField(read_only=True), read_only=True, default=[] + ) + tlp = rfs.CharField(read_only=True) + total_jobs = rfs.IntegerField(read_only=True) + jobs = rfs.PrimaryKeyRelatedField(many=True, read_only=True) + status = rfs.CharField(read_only=True) + owner = rfs.HiddenField(default=rfs.CurrentUserDefault()) + + class Meta: + model = Investigation + fields = rfs.ALL_FIELDS + + +class InvestigationTreeSerializer(rfs.ModelSerializer): + class Meta: + model = Investigation + fields = ["name", "owner", "jobs"] + + jobs = JobTreeSerializer(many=True) diff --git a/Submodules/IntelOwl/api_app/investigations_manager/signals.py b/Submodules/IntelOwl/api_app/investigations_manager/signals.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/investigations_manager/urls.py b/Submodules/IntelOwl/api_app/investigations_manager/urls.py new file mode 100644 index 0000000..98d71d1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/urls.py @@ -0,0 +1,11 @@ +from django.urls import include, path +from rest_framework import routers + +from .views import InvestigationViewSet + +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"investigation", InvestigationViewSet, basename="investigation") + +urlpatterns = [ + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/investigations_manager/views.py b/Submodules/IntelOwl/api_app/investigations_manager/views.py new file mode 100644 index 0000000..2890f25 --- /dev/null +++ b/Submodules/IntelOwl/api_app/investigations_manager/views.py @@ -0,0 +1,116 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging + +from django.http import HttpRequest +from rest_framework import status +from rest_framework.decorators import action +from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.viewsets import ModelViewSet + +from ..models import Job +from ..permissions import IsObjectOwnerOrSameOrgPermission +from ..views import ModelWithOwnershipViewSet +from .filters import InvestigationFilter +from .models import Investigation +from .serializers import InvestigationSerializer, InvestigationTreeSerializer + +logger = logging.getLogger(__name__) + + +class InvestigationViewSet(ModelWithOwnershipViewSet, ModelViewSet): + permission_classes = [IsAuthenticated, IsObjectOwnerOrSameOrgPermission] + serializer_class = InvestigationSerializer + ordering = ["-start_time"] + queryset = Investigation.objects.all() + filterset_class = InvestigationFilter + ordering_fields = [ + "start_time", + "end_time", + ] + + def get_queryset(self): + return super().get_queryset().prefetch_related("jobs") + + def get_object(self): + obj = super().get_object() + if not obj.for_organization and obj.owner != self.request.user: + raise PermissionDenied("You can't use other people private analyses") + return obj + + def _get_job(self, request): + if "job" not in request.data: + raise ValidationError( + {"detail": "You should set the `job` argument in the data"} + ) + job_pk = request.data.get("job") + try: + job = Job.objects.visible_for_user(self.request.user).get(pk=job_pk) + except Job.DoesNotExist: + raise NotFound(detail=f"Job {job_pk} does not exist") + return job + + @action(methods=["POST"], url_name="add_job", detail=True) + def add_job(self, request, pk): + investigation: Investigation = self.get_object() + job: Job = self._get_job(request) + if not investigation.user_can_edit(job.user): + raise PermissionDenied( + "You do not have permissions to add this job to the investigation" + ) + if not job.is_root(): + raise PermissionDenied("You can add to an investigation only primary jobs") + if job.investigation is None: + job.investigation = investigation + job.save() + # we are possibly changing the status of the investigation + job.investigation.set_correct_status(save=True) + + return Response( + status=status.HTTP_200_OK, + data=InvestigationSerializer(instance=investigation).data, + ) + elif job.investigation_id == investigation.id: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"error": "Job is already part of this investigation"}, + ) + else: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"error": "Job is already part of different investigation"}, + ) + + @action(methods=["POST"], url_name="remove_job", detail=True) + def remove_job(self, request, pk): + investigation: Investigation = self.get_object() + request: HttpRequest + job: Job = self._get_job(request) + if not investigation.user_can_edit(job.user): + raise PermissionDenied( + "You do not have permissions to edit this investigation with that job" + ) + if job.investigation_id != investigation.pk: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={"error": f"You can't remove job {job.id} from investigation"}, + ) + job.investigation = None + job.save() + investigation.refresh_from_db() + # we are possibly changing the status of the investigation + investigation.set_correct_status(save=True) + return Response( + status=status.HTTP_200_OK, + data=InvestigationSerializer(instance=investigation).data, + ) + + @action(methods=["GET"], url_name="graph", detail=True) + def tree(self, request, pk): + obj: Investigation = self.get_object() + return Response( + status=status.HTTP_200_OK, + data=InvestigationTreeSerializer(instance=obj).data, + ) diff --git a/Submodules/IntelOwl/api_app/management/__init__.py b/Submodules/IntelOwl/api_app/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/management/commands/__init__.py b/Submodules/IntelOwl/api_app/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/management/commands/celery_reload.py b/Submodules/IntelOwl/api_app/management/commands/celery_reload.py new file mode 100644 index 0000000..5dc5948 --- /dev/null +++ b/Submodules/IntelOwl/api_app/management/commands/celery_reload.py @@ -0,0 +1,32 @@ +import logging +import shlex +import subprocess + +from django.conf import settings +from django.core.management.base import BaseCommand +from django.utils import autoreload + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + @staticmethod + def add_arguments(parser): + parser.add_argument( + "-c", "--command", type=str, help="Celery command", required=True + ) + + def handle(self, *args, **options): + if not settings.DEBUG: + self.stdout.write(self.style.ERROR("Not runnable in production mode")) + return + logger.info("Starting celery with autoreload") + autoreload.run_with_reloader(self._restart_celery, argument=options["command"]) + + def _restart_celery(self, argument): + self.run("pkill celery") + self.run(f"/usr/local/bin/celery {argument}") + + @staticmethod + def run(cmd): + subprocess.run(shlex.split(cmd), check=True) diff --git a/Submodules/IntelOwl/api_app/management/commands/dump_all_plugins.py b/Submodules/IntelOwl/api_app/management/commands/dump_all_plugins.py new file mode 100644 index 0000000..b02301c --- /dev/null +++ b/Submodules/IntelOwl/api_app/management/commands/dump_all_plugins.py @@ -0,0 +1,75 @@ +from typing import Type + +from django.db.migrations.autodetector import MigrationAutodetector + +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.connectors_manager.models import ConnectorConfig +from api_app.ingestors_manager.models import IngestorConfig +from api_app.management.commands.dumpplugin import Command as DumpPluginCommand +from api_app.models import PythonConfig +from api_app.pivots_manager.models import PivotConfig +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.visualizers_manager.models import VisualizerConfig + + +class Command(DumpPluginCommand): + help = "Create migration file from all plugin in an application" + + @staticmethod + def add_arguments(parser): + parser.add_argument( + "plugin_class", + type=str, + help="Plugin config class to use", + choices=[ + AnalyzerConfig.__name__, + ConnectorConfig.__name__, + VisualizerConfig.__name__, + IngestorConfig.__name__, + PivotConfig.__name__, + PlaybookConfig.__name__, + ], + ) + + @property + def migration_counter(self): + try: + self._migration_counter + except AttributeError: + self._migration_counter = 0 + return self._migration_counter + + @migration_counter.setter + def migration_counter(self, value): + self._migration_counter = value + + def _get_last_migration(self, app): + if app == "api_app": + return "0001_2_initial_squashed" + else: + if self.migration_counter == 0: + if app == "pivots_manager": + return "0001_2_initial_squashed" + else: + return "0001_initial_squashed" + else: + return self.name_file[:-3] + + def _name_file(self, obj, app): + last_migration_number = MigrationAutodetector.parse_number( + self._get_last_migration(app) + ) + if self.migration_counter == 0: + last_migration_number += 1 + return ( + f"{str(int(last_migration_number)).rjust(4, '0')}" + f"_{str(int(self.migration_counter)).rjust(4, '0')}" + f"_{obj.snake_case_name}_{obj.name.lower()}.py" + ) + + def handle(self, *args, **options): + config_class = options["plugin_class"] + class_: Type[PythonConfig] = self.str_to_class[config_class] + for name in class_.objects.values_list("name", flat=True): + super().handle(plugin_name=name, plugin_class=config_class) + self.migration_counter += 1 diff --git a/Submodules/IntelOwl/api_app/management/commands/dumpplugin.py b/Submodules/IntelOwl/api_app/management/commands/dumpplugin.py new file mode 100644 index 0000000..e9c8c24 --- /dev/null +++ b/Submodules/IntelOwl/api_app/management/commands/dumpplugin.py @@ -0,0 +1,282 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import json +from pathlib import PosixPath + +from django.core.management import BaseCommand + +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.analyzers_manager.serializers import AnalyzerConfigSerializerForMigration +from api_app.connectors_manager.models import ConnectorConfig +from api_app.connectors_manager.serializers import ConnectorConfigSerializerForMigration +from api_app.ingestors_manager.models import IngestorConfig +from api_app.ingestors_manager.serializers import IngestorConfigSerializerForMigration +from api_app.models import PluginConfig, PythonConfig +from api_app.pivots_manager.models import PivotConfig +from api_app.pivots_manager.serializers import PivotConfigSerializerForMigration +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.playbooks_manager.serializers import PlaybookConfigSerializerForMigration +from api_app.serializers.plugin import ( + ParameterCompleteSerializer, + PluginConfigCompleteSerializer, +) +from api_app.visualizers_manager.models import VisualizerConfig +from api_app.visualizers_manager.serializers import ( + VisualizerConfigSerializerForMigration, +) + + +class Command(BaseCommand): + help = "Create migration file from plugin saved inside the db" + + @staticmethod + def add_arguments(parser): + parser.add_argument( + "plugin_class", + type=str, + help="Plugin config class to use", + choices=[ + AnalyzerConfig.__name__, + ConnectorConfig.__name__, + VisualizerConfig.__name__, + IngestorConfig.__name__, + PivotConfig.__name__, + PlaybookConfig.__name__, + ], + ) + parser.add_argument( + "plugin_name", + type=str, + help="Plugin config name to use", + ) + + @staticmethod + def _get_serialization(obj: PythonConfig, serializer_class): + obj_data = serializer_class(obj).data + obj_data["model"] = f"{obj._meta.app_label}.{obj._meta.object_name}" + params_data = [] + values_data = [] + if hasattr(obj, "parameters"): + for parameter in obj.parameters.all(): + params_data.append(ParameterCompleteSerializer(parameter).data) + try: + # default value + value = PluginConfig.objects.get( + owner=None, + for_organization=False, + parameter=parameter, + parameter__is_secret=False, + **{f"{obj.snake_case_name}__pk": obj.pk}, + ) + except PluginConfig.DoesNotExist: + ... + else: + values_data.append(PluginConfigCompleteSerializer(value).data) + return obj_data, params_data, values_data + + @staticmethod + def _imports() -> str: + return """from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) +""" + + @staticmethod + def _migrate_template(): + return ( + """ +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True +""" # noqa + + """ +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + +""" # noqa + ) + + @staticmethod + def _reverse_migrate_template(): + return """ +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() +""" # noqa + + def _get_body_template(self): + return """ + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ('api_app', '{0}'), + ('{1}', '{2}'), + ] + + operations = [ + migrations.RunPython( + migrate, reverse_migrate + ) + ] + """ # noqa + + def _body_template(self, app): + return self._get_body_template().format( + self._get_last_migration("api_app"), + app, + self._get_last_migration(app), + ) + + @staticmethod + def _get_last_migration(app): + from django.db.migrations.recorder import MigrationRecorder + + return MigrationRecorder.Migration.objects.filter(app=app).latest("id").name + + def _migration_file(self, obj: PythonConfig, serializer_class, app): + obj_data, param_data, values_data = self._get_serialization( + obj, serializer_class + ) + return """{0} +plugin = {1} + +params = {2} + +values = {3} + +{4} +{5} +{6} + """.format( # noqa + self._imports(), + str(json.loads(json.dumps(obj_data))), + str(json.loads(json.dumps(param_data))), + str(json.loads(json.dumps(values_data))), + self._migrate_template(), + self._reverse_migrate_template(), + self._body_template(app), + ) + + def _name_file(self, obj, app): + from django.db.migrations.autodetector import MigrationAutodetector + + last_migration_number = MigrationAutodetector.parse_number( + self._get_last_migration(app) + ) + return ( + f"{str(int(last_migration_number) + 1).rjust(4, '0')}" + f"_{obj.snake_case_name}_{obj.name.lower()}.py" + ) + + @staticmethod + def _save_file(name_file, content, app): + path = "api_app" / PosixPath(app) / "migrations" / name_file + if path.exists(): + raise RuntimeError( + f"Migration {path} already exists." + f" Please apply migration before create a new one" + ) + with open(path, "w", encoding="utf-8") as f: + f.write(content) + + @property + def str_to_class(self): + return { + klass.__name__: klass + for klass in [ + AnalyzerConfig, + ConnectorConfig, + VisualizerConfig, + IngestorConfig, + PivotConfig, + PlaybookConfig, + ] + } + + @property + def str_to_serializer(self): + return { + AnalyzerConfig.__name__: AnalyzerConfigSerializerForMigration, + ConnectorConfig.__name__: ConnectorConfigSerializerForMigration, + VisualizerConfig.__name__: VisualizerConfigSerializerForMigration, + IngestorConfig.__name__: IngestorConfigSerializerForMigration, + PivotConfig.__name__: PivotConfigSerializerForMigration, + PlaybookConfig.__name__: PlaybookConfigSerializerForMigration, + } + + def handle(self, *args, **options): + config_name = options["plugin_name"] + config_class = options["plugin_class"] + class_ = self.str_to_class[config_class] + serializer_class = self.str_to_serializer[config_class] + + obj: PythonConfig = class_.objects.get(name=config_name) + app = obj._meta.app_label + content = self._migration_file(obj, serializer_class, app) + self.name_file = self._name_file(obj, app) + self._save_file(self.name_file, content, app) + self.stdout.write( + self.style.SUCCESS(f"Migration {self.name_file} created with success") + ) diff --git a/Submodules/IntelOwl/api_app/management/commands/migrate.py b/Submodules/IntelOwl/api_app/management/commands/migrate.py new file mode 100644 index 0000000..0423357 --- /dev/null +++ b/Submodules/IntelOwl/api_app/management/commands/migrate.py @@ -0,0 +1,11 @@ +from django.core.management.base import no_translations +from django.core.management.commands.migrate import Command as MigrateCommand + + +class Command(MigrateCommand): + @no_translations + def handle(self, *args, **options): + super().handle(*args, **options) + from api_app.signals import migrate_finished + + migrate_finished.send(self, **options) diff --git a/Submodules/IntelOwl/api_app/management/commands/update_analyzer.py b/Submodules/IntelOwl/api_app/management/commands/update_analyzer.py new file mode 100644 index 0000000..485bd68 --- /dev/null +++ b/Submodules/IntelOwl/api_app/management/commands/update_analyzer.py @@ -0,0 +1,46 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.core.management import BaseCommand + +from api_app.analyzers_manager.models import AnalyzerConfig + + +class Command(BaseCommand): + help = "Execute celery update task" + + @staticmethod + def add_arguments(parser): + parser.add_argument( + "config_names", + nargs="+", + help="Analyzers config name to use", + ) + + def handle(self, *args, **options): + for analyzer_config in AnalyzerConfig.objects.filter( + name=options["config_name"] + ).annotate_runnable(): + if analyzer_config.runnable: + class_ = analyzer_config.python_class + self.stdout.write( + self.style.SUCCESS(f"Starting update of {analyzer_config.name}") + ) + if class_.update(): + self.stdout.write( + self.style.SUCCESS(f"Finished update of {analyzer_config.name}") + ) + else: + self.stdout.write( + self.style.WARNING( + f"Configuration {analyzer_config.name} " + "does not implement _update method" + ) + ) + else: + self.stdout.write( + self.style.WARNING( + f"Configuration {analyzer_config.name} is not runnable" + ) + ) + self.stdout.write(self.style.SUCCESS("Finish execution")) diff --git a/Submodules/IntelOwl/api_app/migrations/0001_1_initial_squashed.py b/Submodules/IntelOwl/api_app/migrations/0001_1_initial_squashed.py new file mode 100644 index 0000000..fef0c2f --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0001_1_initial_squashed.py @@ -0,0 +1,399 @@ +# Generated by Django 4.2.8 on 2024-02-08 07:51 +import datetime + +import django.contrib.postgres.fields +import django.core.validators +import django.db.migrations.operations.special +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + +import api_app.defaults +import api_app.validators + + +def migrate_bi(apps, schema_editor): + from intel_owl.celery import get_queue_name + + CrontabSchedule = apps.get_model("django_celery_beat", "CrontabSchedule") + PeriodicTask = apps.get_model("django_celery_beat", "PeriodicTask") + + # notification + + c1 = CrontabSchedule.objects.get_or_create(minute=12)[0] + PeriodicTask.objects.create( + name="send_elastic_bi", + task="intel_owl.tasks.send_bi_to_elastic", + crontab=c1, + enabled=settings.ELASTICSEARCH_BI_ENABLED, + queue=get_queue_name("default"), + ) + + +def reverse_migrate_bi(apps, schema_editor): + PeriodicTask = apps.get_model("django_celery_beat", "PeriodicTask") + PeriodicTask.objects.filter(name="send_elastic_bi").delete() + + +def create_default_clients(apps, schema_editor): + # We can't import the Client model directly as it may be a newer + # version than this migration expects. We use the historical version. + Client = apps.get_model("durin", "Client") + # for pyintelowl, custom token_ttl + Client.objects.update_or_create( + name="pyintelowl", token_ttl=datetime.timedelta(weeks=4 * 12 * 10) + ) + # others, default token_ttl + Client.objects.update_or_create(name="web-browser") + _ = Client.objects.get_or_create( + name=settings.REST_DURIN["API_ACCESS_CLIENT_NAME"], + token_ttl=settings.REST_DURIN["API_ACCESS_CLIENT_TOKEN_TTL"], + ) + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("certego_saas_organization", "0001_initial"), + ("django_celery_beat", "0018_improve_crontab_helptext"), + ("durin", "0002_client_throttlerate"), + ] + initial = True + operations = [ + migrations.CreateModel( + name="Tag", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "label", + models.CharField( + max_length=50, + unique=True, + validators=[django.core.validators.MinLengthValidator(4)], + ), + ), + ( + "color", + models.CharField( + max_length=7, + validators=[ + django.core.validators.RegexValidator( + "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", "Hex color" + ) + ], + ), + ), + ], + ), + migrations.CreateModel( + name="Job", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_sample", models.BooleanField(default=False)), + ("md5", models.CharField(max_length=32)), + ("observable_name", models.CharField(blank=True, max_length=512)), + ( + "observable_classification", + models.CharField( + blank=True, + choices=[ + ("ip", "Ip"), + ("url", "Url"), + ("domain", "Domain"), + ("hash", "Hash"), + ("generic", "Generic"), + ("", "Empty"), + ], + max_length=12, + ), + ), + ("file_name", models.CharField(blank=True, max_length=512)), + ("file_mimetype", models.CharField(blank=True, max_length=80)), + ( + "status", + models.CharField( + choices=[ + ("pending", "pending"), + ("running", "running"), + ("analyzers_running", "analyzers_running"), + ("analyzers_completed", "analyzers_completed"), + ("connectors_running", "connectors_running"), + ("connectors_completed", "connectors_completed"), + ("pivots_running", "pivots_running"), + ("pivots_completed", "pivots_completed"), + ("visualizers_running", "visualizers_running"), + ("visualizers_completed", "visualizers_completed"), + ("reported_without_fails", "reported_without_fails"), + ("reported_with_fails", "reported_with_fails"), + ("killed", "killed"), + ("failed", "failed"), + ], + default="pending", + max_length=32, + ), + ), + ( + "received_request_time", + models.DateTimeField(auto_now_add=True, db_index=True), + ), + ("finished_analysis_time", models.DateTimeField(blank=True, null=True)), + ( + "errors", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=900), + blank=True, + default=list, + null=True, + size=None, + ), + ), + ( + "file", + models.FileField( + blank=True, upload_to=api_app.defaults.file_directory_path + ), + ), + ( + "tags", + models.ManyToManyField( + blank=True, related_name="jobs", to="api_app.tag" + ), + ), + ( + "tlp", + models.CharField( + choices=[ + ("CLEAR", "Clear"), + ("GREEN", "Green"), + ("AMBER", "Amber"), + ("RED", "Red"), + ], + default="CLEAR", + max_length=8, + ), + ), + ( + "user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ("process_time", models.FloatField(blank=True, null=True)), + ( + "runtime_configuration", + models.JSONField( + default=api_app.defaults.default_runtime, + validators=[api_app.validators.validate_runtime_configuration], + ), + ), + ( + "scan_check_time", + models.DurationField( + blank=True, default=datetime.timedelta(days=1), null=True + ), + ), + ( + "scan_mode", + models.IntegerField( + choices=[ + (1, "Force New Analysis"), + (2, "Check Previous Analysis"), + ], + default=2, + ), + ), + ("sent_to_bi", models.BooleanField(default=False, editable=False)), + ( + "warnings", + django.contrib.postgres.fields.ArrayField( + base_field=models.TextField(), + blank=True, + default=list, + null=True, + size=None, + ), + ), + ], + options={ + "indexes": [ + models.Index( + fields=["md5", "status"], name="api_app_job_md5_4d2c5e_idx" + ) + ], + }, + ), + migrations.AddIndex( + model_name="job", + index=models.Index( + fields=["sent_to_bi", "-received_request_time"], name="JobBISearch" + ), + ), + migrations.CreateModel( + name="Comment", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("content", models.TextField()), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "job", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="comments", + to="api_app.job", + ), + ), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="comment", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "ordering": ["created_at"], + }, + ), + migrations.RunPython( + code=create_default_clients, + reverse_code=django.db.migrations.operations.special.RunPython.noop, + ), + migrations.RunPython( + code=migrate_bi, + reverse_code=reverse_migrate_bi, + ), + migrations.CreateModel( + name="PythonModule", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("module", models.CharField(db_index=True, max_length=120)), + ( + "base_path", + models.CharField( + db_index=True, + max_length=120, + ), + ), + ( + "health_check_schedule", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="healthcheck_for_%(class)s", + to="django_celery_beat.crontabschedule", + ), + ), + ( + "update_task", + models.OneToOneField( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="update_for_%(class)s", + to="django_celery_beat.periodictask", + ), + ), + ( + "update_schedule", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="update_for_%(class)s", + to="django_celery_beat.crontabschedule", + ), + ), + ], + ), + migrations.AlterModelOptions( + name="pythonmodule", + options={"ordering": ["base_path", "module"]}, + ), + migrations.AlterUniqueTogether( + name="pythonmodule", + unique_together={("module", "base_path")}, + ), + migrations.CreateModel( + name="Parameter", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50)), + ( + "type", + models.CharField( + choices=[ + ("int", "Int"), + ("float", "Float"), + ("str", "Str"), + ("bool", "Bool"), + ("list", "List"), + ("dict", "Dict"), + ], + max_length=10, + ), + ), + ("description", models.TextField(blank=True, default="")), + ("is_secret", models.BooleanField(db_index=True)), + ("required", models.BooleanField()), + ( + "python_module", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="parameters", + to="api_app.pythonmodule", + ), + ), + ], + options={ + "unique_together": {("name", "python_module")}, + }, + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0001_2_initial_squashed.py b/Submodules/IntelOwl/api_app/migrations/0001_2_initial_squashed.py new file mode 100644 index 0000000..8ca2993 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0001_2_initial_squashed.py @@ -0,0 +1,323 @@ +import django.db.migrations.operations.special +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + dependencies = [ + ("api_app", "0001_1_initial_squashed"), + ("analyzers_manager", "0001_initial_squashed"), + ("connectors_manager", "0001_initial_squashed"), + ("visualizers_manager", "0001_initial_squashed"), + ("playbooks_manager", "0001_initial_squashed"), + ("ingestors_manager", "0001_initial_squashed"), + ("pivots_manager", "0001_1_initial_squashed"), + ] + operations = [ + migrations.CreateModel( + name="PluginConfig", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("value", models.JSONField(blank=True, null=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "owner", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="custom_configs", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "parameter", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="api_app.parameter", + ), + ), + ("for_organization", models.BooleanField(default=False)), + ( + "pivot_config", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="pivots_manager.pivotconfig", + ), + ), + ( + "visualizer_config", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="visualizers_manager.visualizerconfig", + ), + ), + ( + "ingestor_config", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="ingestors_manager.ingestorconfig", + ), + ), + ( + "connector_config", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="connectors_manager.connectorconfig", + ), + ), + ( + "analyzer_config", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="analyzers_manager.analyzerconfig", + ), + ), + ], + ), + migrations.AddIndex( + model_name="pluginconfig", + index=models.Index( + fields=["owner", "for_organization", "parameter"], + name="api_app_plu_owner_i_55ca9b_idx", + ), + ), + migrations.AddIndex( + model_name="pluginconfig", + index=models.Index( + fields=["owner", "for_organization"], + name="api_app_plu_owner_i_c6658a_idx", + ), + ), + migrations.AlterUniqueTogether( + name="pluginconfig", + unique_together=set(), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", False)), + fields=("owner", "for_organization", "parameter", "analyzer_config"), + name="plugin_config_unique_with_analyzer_config_owner", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("for_organization", "parameter", "analyzer_config"), + name="plugin_config_unique_with_analyzer_config", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", False)), + fields=("owner", "for_organization", "parameter", "connector_config"), + name="plugin_config_unique_with_connector_config_owner", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("for_organization", "parameter", "connector_config"), + name="plugin_config_unique_with_connector_config", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", False)), + fields=("owner", "for_organization", "parameter", "visualizer_config"), + name="plugin_config_unique_with_visualizer_config_owner", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("for_organization", "parameter", "visualizer_config"), + name="plugin_config_unique_with_visualizer_config", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", False)), + fields=("owner", "for_organization", "parameter", "ingestor_config"), + name="plugin_config_unique_with_ingestor_config_owner", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("for_organization", "parameter", "ingestor_config"), + name="plugin_config_unique_with_ingestor_config", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.CheckConstraint( + check=models.Q( + ("analyzer_config__isnull", True), + ("connector_config__isnull", True), + ("visualizer_config__isnull", True), + ("ingestor_config__isnull", True), + ("pivot_config__isnull", True), + _connector="OR", + ), + name="plugin_config_no_config_all_null", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", False)), + fields=("owner", "for_organization", "parameter", "pivot_config"), + name="plugin_config_unique_with_pivot_config_owner", + ), + ), + migrations.AddConstraint( + model_name="pluginconfig", + constraint=models.UniqueConstraint( + condition=models.Q(("owner__isnull", True)), + fields=("for_organization", "parameter", "pivot_config"), + name="plugin_config_unique_with_pivot_config", + ), + ), + migrations.AddField( + model_name="job", + name="analyzers_requested", + field=models.ManyToManyField( + blank=True, + related_name="requested_in_jobs", + to="analyzers_manager.analyzerconfig", + ), + ), + migrations.AddField( + model_name="job", + name="analyzers_to_execute", + field=models.ManyToManyField( + blank=True, + related_name="executed_in_jobs", + to="analyzers_manager.analyzerconfig", + ), + ), + migrations.AddField( + model_name="job", + name="connectors_requested", + field=models.ManyToManyField( + blank=True, + related_name="requested_in_jobs", + to="connectors_manager.connectorconfig", + ), + ), + migrations.AddField( + model_name="job", + name="connectors_to_execute", + field=models.ManyToManyField( + blank=True, + related_name="executed_in_jobs", + to="connectors_manager.connectorconfig", + ), + ), + migrations.AddField( + model_name="job", + name="playbook_requested", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="requested_in_jobs", + to="playbooks_manager.playbookconfig", + ), + ), + migrations.AddField( + model_name="job", + name="playbook_to_execute", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="executed_in_jobs", + to="playbooks_manager.playbookconfig", + ), + ), + migrations.AddField( + model_name="job", + name="visualizers_to_execute", + field=models.ManyToManyField( + blank=True, + related_name="executed_in_jobs", + to="visualizers_manager.visualizerconfig", + ), + ), + migrations.AddIndex( + model_name="job", + index=models.Index( + fields=["playbook_to_execute", "finished_analysis_time", "user"], + name="PlaybookConfigOrdering", + ), + ), + migrations.AlterField( + model_name="pythonmodule", + name="base_path", + field=models.CharField( + choices=[ + ( + "api_app.analyzers_manager.observable_analyzers", + "Observable Analyzer", + ), + ( + "api_app.analyzers_manager.file_analyzers", + "File Analyzer", + ), + ("api_app.connectors_manager.connectors", "Connector"), + ("api_app.ingestors_manager.ingestors", "Ingestor"), + ("api_app.visualizers_manager.visualizers", "Visualizer"), + ("api_app.pivots_manager.pivots", "Pivot"), + ], + db_index=True, + max_length=120, + ), + ), + migrations.AlterField( + model_name="pluginconfig", + name="owner", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0055_organizationpluginconfiguration_and_more.py b/Submodules/IntelOwl/api_app/migrations/0055_organizationpluginconfiguration_and_more.py new file mode 100644 index 0000000..ee78ed8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0055_organizationpluginconfiguration_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.8 on 2024-01-09 11:01 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("contenttypes", "0002_remove_content_type_name"), + ("django_celery_beat", "0018_improve_crontab_helptext"), + ("certego_saas_organization", "0002_membership_is_admin"), + ("api_app", "0001_2_initial_squashed"), + ] + + operations = [ + migrations.CreateModel( + name="OrganizationPluginConfiguration", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.IntegerField()), + ("disabled", models.BooleanField(default=False)), + ("disabled_comment", models.TextField(blank=True, default="")), + ( + "rate_limit_timeout", + models.DurationField( + blank=True, + null=True, + help_text="Expects data in the format 'DD HH:MM:SS'", + ), + ), + ], + ), + migrations.AddField( + model_name="organizationpluginconfiguration", + name="content_type", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + migrations.AddField( + model_name="organizationpluginconfiguration", + name="organization", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="certego_saas_organization.organization", + ), + ), + migrations.AddField( + model_name="organizationpluginconfiguration", + name="rate_limit_enable_task", + field=models.ForeignKey( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="django_celery_beat.periodictask", + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0056_alter_organizationpluginconfiguration_content_type.py b/Submodules/IntelOwl/api_app/migrations/0056_alter_organizationpluginconfiguration_content_type.py new file mode 100644 index 0000000..2ef8537 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0056_alter_organizationpluginconfiguration_content_type.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.8 on 2024-01-09 13:32 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("contenttypes", "0002_remove_content_type_name"), + ("api_app", "0055_organizationpluginconfiguration_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="organizationpluginconfiguration", + name="content_type", + field=models.ForeignKey( + limit_choices_to={ + "model__endswith": "config", + "app_label__endswith": "manager", + }, + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0057_2_change_primary_key.py b/Submodules/IntelOwl/api_app/migrations/0057_2_change_primary_key.py new file mode 100644 index 0000000..4892c6a --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0057_2_change_primary_key.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("visualizers_manager", "0036_1_change_primary_key"), + ("analyzers_manager", "0058_1_change_primary_key"), + ("connectors_manager", "0029_1_change_primary_key"), + ("ingestors_manager", "0016_1_change_primary_key"), + ("api_app", "0056_alter_organizationpluginconfiguration_content_type"), + ] + + operations = [ + migrations.AlterField( + model_name="pluginconfig", + name="visualizer_config", + field=models.CharField(max_length=100, null=True, blank=True), + ), + migrations.AlterField( + model_name="pluginconfig", + name="analyzer_config", + field=models.CharField(max_length=100, null=True, blank=True), + ), + migrations.AlterField( + model_name="pluginconfig", + name="connector_config", + field=models.CharField(max_length=100, null=True, blank=True), + ), + migrations.AlterField( + model_name="pluginconfig", + name="ingestor_config", + field=models.CharField(max_length=100, null=True, blank=True), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0057_4_change_primary_key.py b/Submodules/IntelOwl/api_app/migrations/0057_4_change_primary_key.py new file mode 100644 index 0000000..d1c7978 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0057_4_change_primary_key.py @@ -0,0 +1,157 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PluginConfig = apps.get_model("api_app", "PluginConfig") + VisualizerConfig = apps.get_model("visualizers_manager", "VisualizerConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + ConnectorConfig = apps.get_model("connectors_manager", "ConnectorConfig") + IngestorConfig = apps.get_model("ingestors_manager", "IngestorConfig") + name_visualizer = VisualizerConfig.objects.filter( + name=models.OuterRef("old_visualizer_config") + ).values_list("pk")[:1] + name_analyzer = AnalyzerConfig.objects.filter( + name=models.OuterRef("old_analyzer_config") + ).values_list("pk")[:1] + name_connector = ConnectorConfig.objects.filter( + name=models.OuterRef("old_connector_config") + ).values_list("pk")[:1] + name_ingestor = IngestorConfig.objects.filter( + name=models.OuterRef("old_ingestor_config") + ).values_list("pk")[:1] + PluginConfig.objects.update( + visualizer_config=models.Subquery(name_visualizer), + connector_config=models.Subquery(name_connector), + analyzer_config=models.Subquery(name_analyzer), + ingestor_config=models.Subquery(name_ingestor), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("visualizers_manager", "0036_3_change_primary_key"), + ("analyzers_manager", "0058_3_change_primary_key"), + ("connectors_manager", "0029_3_change_primary_key"), + ("ingestors_manager", "0016_3_change_primary_key"), + ("api_app", "0057_2_change_primary_key"), + ] + + operations = [ + migrations.RenameField( + model_name="pluginconfig", + old_name="visualizer_config", + new_name="old_visualizer_config", + ), + migrations.RenameField( + model_name="pluginconfig", + old_name="analyzer_config", + new_name="old_analyzer_config", + ), + migrations.RenameField( + model_name="pluginconfig", + old_name="connector_config", + new_name="old_connector_config", + ), + migrations.RenameField( + model_name="pluginconfig", + old_name="ingestor_config", + new_name="old_ingestor_config", + ), + migrations.AddField( + model_name="pluginconfig", + name="visualizer_config", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="visualizers_manager.visualizerconfig", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="pluginconfig", + name="analyzer_config", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="analyzers_manager.analyzerconfig", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="pluginconfig", + name="connector_config", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="connectors_manager.connectorconfig", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="pluginconfig", + name="ingestor_config", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="ingestors_manager.ingestorconfig", + ), + preserve_default=False, + ), + migrations.RunPython( + migrate, + ), + migrations.RemoveField(model_name="pluginconfig", name="old_visualizer_config"), + migrations.RemoveField( + model_name="pluginconfig", + name="old_analyzer_config", + ), + migrations.RemoveField( + model_name="pluginconfig", + name="old_connector_config", + ), + migrations.RemoveField( + model_name="pluginconfig", + name="old_ingestor_config", + ), + ] + [ + migrations.RunSQL( + # contraints + column + f'ALTER TABLE "api_app_job_{field}" ADD COLUMN "{model}_id2" BIGINT ' + f'CONSTRAINT "job_{field}_constraint_pk" REFERENCES "{table}_{model}"("id") ' + "DEFERRABLE INITIALLY DEFERRED;" + f'SET CONSTRAINTS "job_{field}_constraint_pk" IMMEDIATE;' + # populate + f'UPDATE "api_app_job_{field}" as "SURROGATA" SET {model}_id2 = (' + f'SELECT "CONFIG"."id" FROM "{table}_{model}" AS "CONFIG" WHERE "CONFIG"."name" = ' + f'"SURROGATA"."{model}_id"' + ') WHERE "SURROGATA"."job_id" IS NOT NULL; ' + # unique + f'ALTER TABLE "api_app_job_{field}" ADD CONSTRAINT ' + f'"job_{field}_constraint_pk_unique" UNIQUE("job_id", "{model}_id2");' + # delete old field + f'ALTER TABLE "api_app_job_{field}" DROP COLUMN "{model}_id";' + # rename + f'ALTER TABLE "api_app_job_{field}" RENAME COLUMN "{model}_id2" TO "{model}_id";' + ) + for field, table, model in [ + ("analyzers_to_execute", "analyzers_manager", "analyzerconfig"), + ("analyzers_requested", "analyzers_manager", "analyzerconfig"), + ("connectors_to_execute", "connectors_manager", "connectorconfig"), + ("connectors_requested", "connectors_manager", "connectorconfig"), + ("visualizers_to_execute", "visualizers_manager", "visualizerconfig"), + ] + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0058_2_change_primary_key.py b/Submodules/IntelOwl/api_app/migrations/0058_2_change_primary_key.py new file mode 100644 index 0000000..93319e8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0058_2_change_primary_key.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_1_change_primary_key"), + ("api_app", "0057_4_change_primary_key"), + ] + + operations = [ + migrations.AlterField( + model_name="job", + name="playbook_requested", + field=models.CharField(max_length=100, null=True, blank=True), + ), + migrations.AlterField( + model_name="job", + name="playbook_to_execute", + field=models.CharField(max_length=100, null=True, blank=True), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0058_4_change_primary_key.py b/Submodules/IntelOwl/api_app/migrations/0058_4_change_primary_key.py new file mode 100644 index 0000000..cb21740 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0058_4_change_primary_key.py @@ -0,0 +1,69 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + Job = apps.get_model("api_app", "Job") + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + Job.objects.update( + playbook_to_execute=models.Subquery( + PlaybookConfig.objects.filter( + name=models.OuterRef("old_playbook_to_execute") + ).values_list("pk")[:1] + ), + playbook_requested=models.Subquery( + PlaybookConfig.objects.filter( + name=models.OuterRef("old_playbook_requested") + ).values_list("pk")[:1] + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_3_change_primary_key"), + ("api_app", "0058_2_change_primary_key"), + ] + + operations = [ + migrations.RenameField( + model_name="job", + old_name="playbook_requested", + new_name="old_playbook_requested", + ), + migrations.RenameField( + model_name="job", + old_name="playbook_to_execute", + new_name="old_playbook_to_execute", + ), + migrations.AddField( + model_name="job", + name="playbook_to_execute", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="executed_in_jobs", + to="playbooks_manager.playbookconfig", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="job", + name="playbook_requested", + field=models.ForeignKey( + default=None, + null=True, + blank=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="requested_in_jobs", + to="playbooks_manager.playbookconfig", + ), + preserve_default=False, + ), + migrations.RunPython(migrate), + migrations.RemoveField(model_name="job", name="old_playbook_requested"), + migrations.RemoveField(model_name="job", name="old_playbook_to_execute"), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0059_alter_organizationpluginconfiguration_unique_together.py b/Submodules/IntelOwl/api_app/migrations/0059_alter_organizationpluginconfiguration_unique_together.py new file mode 100644 index 0000000..f4c3502 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0059_alter_organizationpluginconfiguration_unique_together.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.8 on 2024-01-19 08:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("certego_saas_organization", "0002_membership_is_admin"), + ("contenttypes", "0002_remove_content_type_name"), + ("api_app", "0058_4_change_primary_key"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="organizationpluginconfiguration", + unique_together={("object_id", "organization", "content_type")}, + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0060_job_depth_job_numchild_job_path.py b/Submodules/IntelOwl/api_app/migrations/0060_job_depth_job_numchild_job_path.py new file mode 100644 index 0000000..6fb999e --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0060_job_depth_job_numchild_job_path.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.8 on 2024-01-30 14:31 +import django +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0059_alter_organizationpluginconfiguration_unique_together"), + ] + + operations = [ + migrations.AddField( + model_name="job", + name="depth", + field=models.PositiveIntegerField(default=1), + preserve_default=False, + ), + migrations.AddField( + model_name="job", + name="numchild", + field=models.PositiveIntegerField(default=0), + ), + migrations.AddField( + model_name="job", + name="path", + field=models.CharField(default=1, max_length=255), + preserve_default=False, + ), + migrations.AddField( + model_name="job", + name="investigation", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="jobs", + to="investigations_manager.investigation", + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0061_job_depth_analysis.py b/Submodules/IntelOwl/api_app/migrations/0061_job_depth_analysis.py new file mode 100644 index 0000000..85d8667 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0061_job_depth_analysis.py @@ -0,0 +1,74 @@ +# Generated by Django 4.2.8 on 2024-01-30 14:31 +from django.db import migrations, models +from django.db.models import CharField, F, Value +from django.db.models.functions import Concat + + +def migrate(apps, schema_editor): + # I have to use the import here because i really need the class methods + + PivotMap = apps.get_model("pivots_manager", "PivotMap") + Investigation = apps.get_model("investigations_manager", "Investigation") + # I have to use the import here because i really need the class methods + from api_app.models import Job as JobNonStoric + + last_root = JobNonStoric.objects.order_by("pk").filter(pivot_parent=None).first() + if last_root: + last_root.path = last_root._get_path(None, 1, 1) + last_root.save() + # all the other roots + for job in ( + JobNonStoric.objects.exclude(pk=last_root.pk) + .filter(pivot_parent=None) + .iterator() + ): + newpath = last_root._inc_path() + job.path = newpath + job.save() + last_root = job + + # we are now setting all the children + for obj in PivotMap.objects.all().iterator(): + parent = obj.starting_job # parent + child = obj.ending_job + parent.numchild += 1 + parent.save() + child.depth = 2 + child.path = JobNonStoric._get_path(parent.path, child.depth, 1) + child.save() + if parent.numchild == 1: + an = Investigation.objects.create( + name="Pivot investigation", + owner=parent.user, + start_time=parent.received_request_time, + end_time=child.finished_analysis_time, + status="concluded", + ) + an.jobs.add(parent) + + Investigation.objects.update( + name=Concat(F("name"), Value(" #"), F("pk"), output_field=CharField()) + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0060_job_depth_job_numchild_job_path"), + ("pivots_manager", "0025_alter_pivotmap_ending_job"), + ("investigations_manager", "0001_initial"), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=[ + migrations.RunPython(migrate, migrations.RunPython.noop), + ], + state_operations=[ + migrations.AlterField( + model_name="job", + name="path", + field=models.CharField(max_length=255, unique=True), + ), + ], + ) + ] diff --git a/Submodules/IntelOwl/api_app/migrations/0062_alter_parameter_python_module.py b/Submodules/IntelOwl/api_app/migrations/0062_alter_parameter_python_module.py new file mode 100644 index 0000000..c07d000 --- /dev/null +++ b/Submodules/IntelOwl/api_app/migrations/0062_alter_parameter_python_module.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.11 on 2024-03-28 11:41 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0061_job_depth_analysis"), + ] + + operations = [ + migrations.AlterField( + model_name="parameter", + name="python_module", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="parameters", + to="api_app.pythonmodule", + ), + ), + migrations.AlterField( + model_name="job", + name="path", + field=models.CharField(max_length=255, unique=True), + ), + ] diff --git a/Submodules/IntelOwl/api_app/migrations/__init__.py b/Submodules/IntelOwl/api_app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/mixins.py b/Submodules/IntelOwl/api_app/mixins.py new file mode 100644 index 0000000..24c2845 --- /dev/null +++ b/Submodules/IntelOwl/api_app/mixins.py @@ -0,0 +1,50 @@ +import logging + +from django.core.cache import cache +from rest_framework.response import Response + +from certego_saas.ext.pagination import CustomPageNumberPagination + +logger = logging.getLogger(__name__) + + +class PaginationMixin: + pagination_class = CustomPageNumberPagination + + def list(self, request, *args, **kwargs): + cache_name = ( + f"list_{self.serializer_class.Meta.model.__name__}_{request.user.username}" + ) + queryset = self.filter_queryset(self.get_queryset()) + page = self.paginate_queryset(queryset) + + if page is not None: + objects = queryset.filter(pk__in=[plugin.pk for plugin in page]) + if "page" in request.query_params and "page_size" in request.query_params: + cache_name += ( + f"_{request.query_params['page']}_" + f"{request.query_params['page_size']}" + ) + cache_hit = cache.get(cache_name) + if cache_hit is None: + logger.debug(f"View {cache_name} cache not hit") + serializer = self.get_serializer(objects, many=True) + data = serializer.data + cache.set(cache_name, value=data, timeout=60 * 60 * 24 * 7) + else: + logger.debug(f"View {cache_name} cache hit") + data = cache_hit + cache.touch(cache_name, timeout=60 * 60 * 24 * 7) + return self.get_paginated_response(data) + else: + cache_hit = cache.get(cache_name) + + if cache_hit is None: + serializer = self.get_serializer(queryset, many=True) + data = serializer.data + cache.set(cache_name, value=data, timeout=60 * 60 * 24 * 7) + else: + data = cache_hit + cache.touch(cache_name, timeout=60 * 60 * 24 * 7) + + return Response(data) diff --git a/Submodules/IntelOwl/api_app/models.py b/Submodules/IntelOwl/api_app/models.py new file mode 100644 index 0000000..545b137 --- /dev/null +++ b/Submodules/IntelOwl/api_app/models.py @@ -0,0 +1,1278 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import base64 +import datetime +import json +import logging +import typing +import uuid +from typing import TYPE_CHECKING, Any, Dict, Optional, Type + +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation +from django.contrib.contenttypes.models import ContentType +from django.utils.timezone import now +from django_celery_beat.models import ClockedSchedule, CrontabSchedule, PeriodicTask +from treebeard.mp_tree import MP_Node + +from api_app.interfaces import OwnershipAbstractModel + +if TYPE_CHECKING: + from api_app.serializers import PythonConfigSerializer + +from celery import group +from celery.canvas import Signature +from django.conf import settings +from django.contrib.postgres import fields as pg_fields +from django.core.cache import cache +from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.core.validators import MinLengthValidator, MinValueValidator, RegexValidator +from django.db import models +from django.db.models import BaseConstraint, Q, QuerySet, UniqueConstraint +from django.urls import reverse +from django.utils import timezone +from django.utils.functional import cached_property +from django.utils.module_loading import import_string + +from api_app.choices import ( + TLP, + ObservableClassification, + ParamTypes, + PythonModuleBasePaths, + ReportStatus, + ScanMode, + Status, +) + +if typing.TYPE_CHECKING: + from api_app.classes import Plugin + +from api_app.defaults import default_runtime, file_directory_path +from api_app.helpers import calculate_sha1, calculate_sha256, deprecated, get_now +from api_app.queryset import ( + AbstractConfigQuerySet, + AbstractReportQuerySet, + JobQuerySet, + OrganizationPluginConfigurationQuerySet, + ParameterQuerySet, + PluginConfigQuerySet, + PythonConfigQuerySet, +) +from api_app.validators import plugin_name_validator, validate_runtime_configuration +from certego_saas.apps.organization.organization import Organization +from certego_saas.models import User +from intel_owl import tasks +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +class PythonModule(models.Model): + module = models.CharField(max_length=120, db_index=True) + base_path = models.CharField( + max_length=120, db_index=True, choices=PythonModuleBasePaths.choices + ) + update_schedule = models.ForeignKey( + CrontabSchedule, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="update_for_%(class)s", + ) + update_task = models.OneToOneField( + PeriodicTask, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="update_for_%(class)s", + editable=False, + ) + + health_check_schedule = models.ForeignKey( + CrontabSchedule, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="healthcheck_for_%(class)s", + ) + + class Meta: + unique_together = [["module", "base_path"]] + ordering = ["base_path", "module"] + + def __str__(self): + return self.module + + def __contains__(self, item: str): + if not isinstance(item, str) and not isinstance(item, PythonConfig): + raise TypeError(f"{self.__class__.__name__} needs a string or pythonConfig") + if isinstance(item, str): + return item in self.python_complete_path + elif isinstance(item, PythonConfig): + return self.configs.filter(name=item.name).exists() + + @cached_property + def python_complete_path(self) -> str: + return f"{self.base_path}.{self.module}" + + @property + def disabled(self): + # it is disabled if it does not exist a configuration enabled + return not self.configs.filter(disabled=False).exists() + + @cached_property + def python_class(self) -> Type["Plugin"]: + return import_string(self.python_complete_path) + + @property + def configs(self) -> PythonConfigQuerySet: + return self.config_class.objects.filter(python_module__pk=self.pk) + + @cached_property + def config_class(self) -> Type["PythonConfig"]: + return self.python_class.config_model + + @property + def queue(self) -> str: + try: + return self.configs.order_by("?").first().queue + except AttributeError: + return None + + def _clean_python_module(self): + try: + _ = self.python_class + except ImportError as exc: + raise ValidationError( + "`python_module` incorrect, " + f"{self.python_complete_path} couldn't be imported" + ) from exc + + def clean(self) -> None: + super().clean() + self._clean_python_module() + + def generate_update_periodic_task(self): + from intel_owl.tasks import update + + if hasattr(self, "update_schedule") and self.update_schedule: + enabled = settings.REPO_DOWNLOADER_ENABLED and not self.disabled + periodic_task = PeriodicTask.objects.update_or_create( + name=f"{self.python_complete_path}Update", + task=f"{update.__module__}.{update.__name__}", + defaults={ + "crontab": self.update_schedule, + "queue": self.queue, + "enabled": enabled, + "kwargs": json.dumps({"python_module_pk": self.pk}), + }, + )[0] + self.update_task = periodic_task + + +class Tag(models.Model): + label = models.CharField( + max_length=50, + blank=False, + null=False, + unique=True, + validators=[MinLengthValidator(4)], + ) + color = models.CharField( + max_length=7, + blank=False, + null=False, + validators=[RegexValidator(r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$", "Hex color")], + ) + + def __str__(self): + return self.label + + +class Comment(models.Model): + # make the user null if the user is deleted + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="comment", + ) + + class Meta: + ordering = ["created_at"] + + job = models.ForeignKey( + "Job", + on_delete=models.CASCADE, + related_name="comments", + ) + content = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + +class Job(MP_Node): + objects = JobQuerySet.as_manager() + + class Meta: + indexes = [ + models.Index( + fields=[ + "md5", + "status", + ] + ), + models.Index( + fields=["playbook_to_execute", "finished_analysis_time", "user"], + name="PlaybookConfigOrdering", + ), + models.Index( + fields=["sent_to_bi", "-received_request_time"], name="JobBISearch" + ), + ] + + # constants + TLP = TLP + Status = Status + investigation = models.ForeignKey( + "investigations_manager.Investigation", + on_delete=models.PROTECT, + related_name="jobs", + null=True, + blank=True, + default=None, + ) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + null=True, # for backwards compatibility + ) + is_sample = models.BooleanField(blank=False, default=False) + md5 = models.CharField(max_length=32, blank=False) + observable_name = models.CharField(max_length=512, blank=True) + observable_classification = models.CharField( + max_length=12, blank=True, choices=ObservableClassification.choices + ) + file_name = models.CharField(max_length=512, blank=True) + file_mimetype = models.CharField(max_length=80, blank=True) + status = models.CharField( + max_length=32, blank=False, choices=Status.choices, default="pending" + ) + + analyzers_requested = models.ManyToManyField( + "analyzers_manager.AnalyzerConfig", related_name="requested_in_jobs", blank=True + ) + connectors_requested = models.ManyToManyField( + "connectors_manager.ConnectorConfig", + related_name="requested_in_jobs", + blank=True, + ) + playbook_requested = models.ForeignKey( + "playbooks_manager.PlaybookConfig", + related_name="requested_in_jobs", + null=True, + blank=True, + on_delete=models.SET_NULL, + ) + + analyzers_to_execute = models.ManyToManyField( + "analyzers_manager.AnalyzerConfig", related_name="executed_in_jobs", blank=True + ) + connectors_to_execute = models.ManyToManyField( + "connectors_manager.ConnectorConfig", + related_name="executed_in_jobs", + blank=True, + ) + visualizers_to_execute = models.ManyToManyField( + "visualizers_manager.VisualizerConfig", + related_name="executed_in_jobs", + blank=True, + ) + playbook_to_execute = models.ForeignKey( + "playbooks_manager.PlaybookConfig", + related_name="executed_in_jobs", + null=True, + blank=True, + on_delete=models.SET_NULL, + ) + runtime_configuration = models.JSONField( + blank=False, + default=default_runtime, + null=False, + validators=[validate_runtime_configuration], + ) + received_request_time = models.DateTimeField(auto_now_add=True, db_index=True) + finished_analysis_time = models.DateTimeField(blank=True, null=True) + process_time = models.FloatField(blank=True, null=True) + tlp = models.CharField(max_length=8, choices=TLP.choices, default=TLP.CLEAR) + errors = pg_fields.ArrayField( + models.CharField(max_length=900), blank=True, default=list, null=True + ) + warnings = pg_fields.ArrayField( + models.TextField(), blank=True, default=list, null=True + ) + file = models.FileField(blank=True, upload_to=file_directory_path) + tags = models.ManyToManyField(Tag, related_name="jobs", blank=True) + + scan_mode = models.IntegerField( + choices=ScanMode.choices, + null=False, + blank=False, + default=ScanMode.CHECK_PREVIOUS_ANALYSIS.value, + ) + scan_check_time = models.DurationField( + null=True, blank=True, default=datetime.timedelta(hours=24) + ) + sent_to_bi = models.BooleanField(editable=False, default=False) + + def __str__(self): + return f'{self.__class__.__name__}(#{self.pk}, "{self.analyzed_object_name}")' + + def get_root(self): + if self.is_root(): + return self + try: + return super().get_root() + except self.MultipleObjectsReturned: + # django treebeard is not thread safe + # this is not a really valid solution, but it will work for now + return self.objects.filter(path=self.path[0 : self.steplen]).first() # noqa + + @property + def analyzed_object_name(self): + return self.file_name if self.is_sample else self.observable_name + + @property + def analyzed_object(self): + return self.file if self.is_sample else self.observable_name + + @cached_property + def sha256(self) -> str: + return calculate_sha256( + self.file.read() if self.is_sample else self.observable_name.encode("utf-8") + ) + + @cached_property + def parent_job(self) -> Optional["Job"]: + return self.get_parent() + + @cached_property + def sha1(self) -> str: + return calculate_sha1( + self.file.read() if self.is_sample else self.observable_name.encode("utf-8") + ) + + @cached_property + def b64(self) -> str: + return base64.b64encode( + self.file.read() if self.is_sample else self.observable_name.encode("utf-8") + ).decode("utf-8") + + def get_absolute_url(self): + return self.get_absolute_url_by_pk(self.pk) + + @classmethod + def get_absolute_url_by_pk(cls, pk: int): + return reverse("jobs-detail", args=[pk]).removeprefix("/api") + + @property + def url(self): + return settings.WEB_CLIENT_URL + self.get_absolute_url() + + def retry(self): + self.status = self.Status.RUNNING + self.save(update_fields=["status"]) + + runner = self._get_pipeline( + analyzers=self.analyzerreports.filter_retryable().get_configurations(), + pivots=self.pivotreports.filter_retryable().get_configurations(), + connectors=self.connectorreports.filter_retryable().get_configurations(), + visualizers=self.visualizerreports.filter_retryable().get_configurations(), + ) + + runner.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=self.priority, + ) + + def set_final_status(self) -> None: + logger.info(f"[STARTING] set_final_status for <-- {self}.") + + if self.status == self.Status.FAILED: + logger.error( + f"[REPORT] {self}, status: failed. " "Do not process the report" + ) + else: + stats = self._get_config_reports_stats() + logger.info(f"[REPORT] {self}, status:{self.status}, reports:{stats}") + + if stats["success"] == stats["all"]: + self.status = self.Status.REPORTED_WITHOUT_FAILS + elif stats["failed"] == stats["all"]: + self.status = self.Status.FAILED + elif stats["killed"] == stats["all"]: + self.status = self.Status.KILLED + elif stats["failed"] >= 1 or stats["killed"] >= 1: + self.status = self.Status.REPORTED_WITH_FAILS + + self.finished_analysis_time = get_now() + + logger.info(f"{self.__repr__()} setting status to {self.status}") + self.save( + update_fields=[ + "status", + "errors", + "finished_analysis_time", + ] + ) + try: + # we update the status of the analysis + if root_investigation := self.get_root().investigation: + root_investigation.set_correct_status(save=True) + except Exception as e: + logger.exception( + f"investigation status not updated. Job: {self.pk}. Error: {e}" + ) + + def __get_config_reports(self, config: typing.Type["AbstractConfig"]) -> QuerySet: + return getattr(self, f"{config.__name__.split('Config')[0].lower()}reports") + + def __get_config_to_execute( + self, config: typing.Type["AbstractConfig"] + ) -> QuerySet: + return getattr( + self, f"{config.__name__.split('Config')[0].lower()}s_to_execute" + ) + + def __get_single_config_reports_stats( + self, config: typing.Type["AbstractConfig"] + ) -> typing.Dict: + reports = self.__get_config_reports(config) + aggregators = { + s.lower(): models.Count("status", filter=models.Q(status=s)) + for s in AbstractReport.Status.values + } + return reports.aggregate( + all=models.Count("status"), + **aggregators, + ) + + def _get_config_reports_stats(self) -> typing.Dict: + from api_app.analyzers_manager.models import AnalyzerConfig + from api_app.connectors_manager.models import ConnectorConfig + from api_app.visualizers_manager.models import VisualizerConfig + + result = {} + for config in [AnalyzerConfig, ConnectorConfig, VisualizerConfig]: + partial_result = self.__get_single_config_reports_stats(config) + # merge them + result = { + k: result.get(k, 0) + partial_result.get(k, 0) + for k in set(result) | set(partial_result) + } + return result + + def kill_if_ongoing(self): + from api_app.analyzers_manager.models import AnalyzerConfig + from api_app.connectors_manager.models import ConnectorConfig + from api_app.visualizers_manager.models import VisualizerConfig + from api_app.websocket import JobConsumer + from intel_owl.celery import app as celery_app + + for config in [AnalyzerConfig, ConnectorConfig, VisualizerConfig]: + reports = self.__get_config_reports(config).filter( + status__in=[ + AbstractReport.Status.PENDING, + AbstractReport.Status.RUNNING, + ] + ) + + ids = list(reports.values_list("task_id", flat=True)) + logger.info(f"We are going to kill tasks {ids}") + # kill celery tasks using task ids + celery_app.control.revoke(ids, terminate=True) + + reports.update(status=self.Status.KILLED) + + self.status = self.Status.KILLED + self.save(update_fields=["status"]) + JobConsumer.serialize_and_send_job(self) + + def _get_signatures(self, queryset: PythonConfigQuerySet) -> Signature: + config_class: PythonConfig = queryset.model + signatures = list( + queryset.annotate_runnable(self.user) + .filter(runnable=True) + .get_signatures(self) + ) + logger.info(f"{config_class} signatures are {signatures}") + + return ( + config_class.signature_pipeline_running(self) + | group(signatures) + | config_class.signature_pipeline_completed(self) + ) + + @cached_property + def pivots_to_execute(self) -> PythonConfigQuerySet: + from api_app.pivots_manager.models import PivotConfig + + if self.playbook_to_execute: + pivots = self.playbook_to_execute.pivots.all() + else: + pivots = PivotConfig.objects.valid( + self.analyzers_to_execute.all(), self.connectors_to_execute.all() + ) + return pivots.annotate_runnable(self.user).filter(runnable=True) + + @property + def _final_status_signature(self) -> Signature: + return tasks.job_set_final_status.signature( + args=[self.pk], + kwargs={}, + queue=get_queue_name(settings.CONFIG_QUEUE), + immutable=True, + MessageGroupId=str(uuid.uuid4()), + priority=self.priority, + ) + + @property + def priority(self): + return self.user.profile.task_priority + + def _get_pipeline( + self, + analyzers: PythonConfigQuerySet, + pivots: PythonConfigQuerySet, + connectors: PythonConfigQuerySet, + visualizers: PythonConfigQuerySet, + ) -> Signature: + runner = self._get_signatures(analyzers.distinct()) + pivots_analyzers = pivots.filter( + related_analyzer_configs__isnull=False + ).distinct() + if pivots_analyzers.exists(): + runner |= self._get_signatures(pivots_analyzers) + if connectors.exists(): + runner |= self._get_signatures(connectors) + pivots_connectors = pivots.filter( + related_connector_configs__isnull=False + ).distinct() + if pivots_connectors.exists(): + runner |= self._get_signatures(pivots_connectors) + if visualizers.exists(): + runner |= self._get_signatures(visualizers) + runner |= self._final_status_signature + return runner + + def execute(self): + self.status = self.Status.RUNNING + self.save(update_fields=["status"]) + runner = self._get_pipeline( + self.analyzers_to_execute.all(), + self.pivots_to_execute.all(), + self.connectors_to_execute.all(), + self.visualizers_to_execute.all(), + ) + runner() + + def get_config_runtime_configuration(self, config: "AbstractConfig") -> typing.Dict: + try: + self.__get_config_to_execute(config.__class__).get(name=config.name) + except config.DoesNotExist: + raise TypeError( + f"{config.__class__.__name__} {config.name} " + f"is not configured inside job {self.pk}" + ) + return self.runtime_configuration.get(config.runtime_configuration_key, {}).get( + config.name, {} + ) + + # user methods + + @classmethod + def user_total_submissions(cls, user: User) -> int: + return user.job_set.count() + + @classmethod + def user_month_submissions(cls, user: User) -> int: + """ + Excludes failed submissions. + """ + return ( + user.job_set.filter( + received_request_time__gte=timezone.now().replace( + day=1, hour=0, minute=0, second=0, microsecond=0 + ) + ) + .exclude(status=cls.Status.FAILED) + .count() + ) + + def clean(self) -> None: + super().clean() + self.clean_scan() + + def clean_scan(self): + if ( + self.scan_mode == ScanMode.FORCE_NEW_ANALYSIS.value + and self.scan_check_time is not None + ): + raise ValidationError( + f"You can't have set mode to {ScanMode.FORCE_NEW_ANALYSIS.name}" + f" and have check_time set to {self.scan_check_time}" + ) + elif ( + self.scan_mode == ScanMode.CHECK_PREVIOUS_ANALYSIS.value + and self.scan_check_time is None + ): + raise ValidationError( + f"You can't have set mode to {ScanMode.CHECK_PREVIOUS_ANALYSIS.name}" + " and not have check_time set" + ) + + +class Parameter(models.Model): + objects = ParameterQuerySet.as_manager() + + name = models.CharField(null=False, blank=False, max_length=50) + type = models.CharField( + choices=ParamTypes.choices, max_length=10, null=False, blank=False + ) + description = models.TextField(blank=True, default="") + is_secret = models.BooleanField(db_index=True) + required = models.BooleanField(null=False) + python_module = models.ForeignKey( + PythonModule, related_name="parameters", on_delete=models.CASCADE + ) + + class Meta: + unique_together = [["name", "python_module"]] + + def __str__(self): + return self.name + + def refresh_cache_keys(self): + self.config_class.delete_class_cache_keys() + for config in self.config_class.objects.filter( + python_module=self.python_module + ): + config: PythonConfig + config.refresh_cache_keys() + + @cached_property + def config_class(self) -> Type["PythonConfig"]: + return self.python_module.python_class.config_model + + +class PluginConfig(OwnershipAbstractModel): + objects = PluginConfigQuerySet.as_manager() + value = models.JSONField(blank=True, null=True) + + parameter = models.ForeignKey( + Parameter, on_delete=models.CASCADE, null=False, related_name="values" + ) + updated_at = models.DateTimeField(auto_now=True) + analyzer_config = models.ForeignKey( + "analyzers_manager.AnalyzerConfig", + related_name="values", + on_delete=models.CASCADE, + null=True, + blank=True, + ) + connector_config = models.ForeignKey( + "connectors_manager.ConnectorConfig", + related_name="values", + on_delete=models.CASCADE, + null=True, + blank=True, + ) + visualizer_config = models.ForeignKey( + "visualizers_manager.VisualizerConfig", + related_name="values", + on_delete=models.CASCADE, + null=True, + blank=True, + ) + ingestor_config = models.ForeignKey( + "ingestors_manager.IngestorConfig", + on_delete=models.CASCADE, + related_name="values", + null=True, + blank=True, + ) + pivot_config = models.ForeignKey( + "pivots_manager.PivotConfig", + on_delete=models.CASCADE, + related_name="values", + null=True, + blank=True, + ) + + class Meta: + constraints: typing.List[BaseConstraint] = [ + models.CheckConstraint( + check=Q(analyzer_config__isnull=True) + | Q(connector_config__isnull=True) + | Q(visualizer_config__isnull=True) + | Q(ingestor_config__isnull=True) + | Q(pivot_config__isnull=True), + name="plugin_config_no_config_all_null", + ) + ] + [ + item + for config in [ + "analyzer_config", + "connector_config", + "visualizer_config", + "ingestor_config", + "pivot_config", + ] + for item in [ + UniqueConstraint( + fields=["owner", "for_organization", "parameter", config], + name=f"plugin_config_unique_with_{config}_owner", + condition=Q(owner__isnull=False), + ), + UniqueConstraint( + fields=["for_organization", "parameter", config], + name=f"plugin_config_unique_with_{config}", + condition=Q(owner__isnull=True), + ), + ] + ] + indexes = [ + models.Index(fields=["owner", "for_organization", "parameter"]), + ] + OwnershipAbstractModel.Meta.indexes + + @cached_property + def config(self) -> "PythonConfig": + return list(filter(None, self._possible_configs()))[0] + + def refresh_cache_keys(self): + try: + _ = self.config and self.owner + except ObjectDoesNotExist: + # this happens if the configuration/user was deleted before this instance + return + if self.owner: + if self.owner.has_membership() and self.owner.membership.is_admin: + for user in User.objects.filter( + membership__organization=self.owner.membership.organization + ): + self.config.delete_class_cache_keys(user) + self.config.refresh_cache_keys(user) + else: + self.owner: User + self.config.delete_class_cache_keys(self.owner) + self.config.refresh_cache_keys(self.owner) + + else: + self.config.delete_class_cache_keys() + self.config.refresh_cache_keys() + + def _possible_configs(self) -> typing.List["PythonConfig"]: + return [ + self.analyzer_config, + self.connector_config, + self.visualizer_config, + self.ingestor_config, + self.pivot_config, + ] + + def clean_config(self) -> None: + if len(list(filter(None, self._possible_configs()))) != 1: + configs = ", ".join( + [config.name for config in self._possible_configs() if config] + ) + if not configs: + raise ValidationError("You must select a plugin configuration") + raise ValidationError(f"You must have exactly one between {configs}") + + def clean_value(self): + from django.forms.fields import JSONString + + if isinstance(self.value, JSONString): + self.value = str(self.value) + if type(self.value).__name__ != self.parameter.type: + raise ValidationError( + f"Type {type(self.value).__name__} is wrong:" + f" should be {self.parameter.type}" + ) + + def clean_parameter(self): + if self.config.python_module != self.parameter.python_module: + raise ValidationError( + f"Missmatch between config python module {self.config.python_module}" + f" and parameter python module {self.parameter.python_module}" + ) + + def clean(self): + super().clean() + self.clean_value() + self.clean_for_organization() + self.clean_config() + self.clean_parameter() + + @property + def attribute(self): + return self.parameter.name + + def is_secret(self): + return self.parameter.is_secret + + @property + def plugin_name(self): + return self.config.name + + @property + def type(self): + # TODO retrocompatibility + return self.config.plugin_type + + @property + def config_type(self): + # TODO retrocompatibility + return "2" if self.is_secret() else "1" + + +class OrganizationPluginConfiguration(models.Model): + objects = OrganizationPluginConfigurationQuerySet.as_manager() + content_type = models.ForeignKey( + ContentType, + on_delete=models.CASCADE, + limit_choices_to={ + "model__endswith": "config", + "app_label__endswith": "manager", + }, + ) + object_id = models.IntegerField() + config = GenericForeignKey("content_type", "object_id") + + organization = models.ForeignKey(Organization, on_delete=models.CASCADE) + + disabled = models.BooleanField(default=False) + disabled_comment = models.TextField(default="", blank=True) + + rate_limit_timeout = models.DurationField( + null=True, blank=True, help_text="Expects data in the format 'DD HH:MM:SS'" + ) + rate_limit_enable_task = models.ForeignKey( + PeriodicTask, on_delete=models.SET_NULL, null=True, blank=True, editable=False + ) + + class Meta: + unique_together = [("object_id", "organization", "content_type")] + + def __str__(self): + return f"{self.config} ({self.organization})" + + def disable_for_rate_limit(self): + self.disabled = True + + enabled_to = now() + self.rate_limit_timeout + self.disabled_comment = ( + "Rate limit hit at " + f"{now().strftime('%d %m %Y: %H %M %S')}.\n" + "Will be enabled back at " + f"{enabled_to.strftime('%d %m %Y: %H %M %S')}" + ) + clock_schedule = ClockedSchedule.objects.get_or_create(clocked_time=enabled_to)[ + 0 + ] + if not self.rate_limit_enable_task: + from intel_owl.tasks import enable_configuration_for_org_for_rate_limit + + self.rate_limit_enable_task = PeriodicTask.objects.create( + name=f"{self.config.name}" + f"-{self.organization.name}" + "RateLimitCleaner", + clocked=clock_schedule, + one_off=True, + enabled=True, + task=f"{enable_configuration_for_org_for_rate_limit.__name__}", + kwargs=json.dumps( + { + "org_configuration_pk": self.pk, + } + ), + ) + else: + self.rate_limit_enable_task.clocked = clock_schedule + self.rate_limit_enable_task.enabled = True + self.rate_limit_enable_task.save() + logger.warning(f"Disabling {self} for rate limit") + self.save() + + def disable_manually(self, user: User): + self.disabled = True + self.disabled_comment = ( + f"Disabled by user {user.username}" + f" at {now().strftime('%Y-%m-%d %H:%M:%S')}" + ) + if self.rate_limit_enable_task: + self.rate_limit_enable_task.delete() + self.save() + + def enable_manually(self, user: User): + self.disabled_comment += ( + f"\nEnabled back by {user.username}" + f" at {now().strftime('%Y-%m-%d %H:%M:%S')}" + ) + self.enable() + + def enable(self): + logger.info(f"Enabling back {self}") + self.disabled = False + self.disabled_comment = "" + self.save() + if self.rate_limit_enable_task: + self.rate_limit_enable_task.delete() + + +class ListCachable(models.Model): + class Meta: + abstract = True + + @classmethod + def delete_class_cache_keys(cls, user: User = None): + base_key = f"{cls.__name__}_{user.username if user else ''}" + for key in cache.get_where(f"list_{base_key}").keys(): + logger.debug(f"Deleting cache key {key}") + cache.delete(key) + + @classmethod + @property + def python_path(cls) -> str: + return f"{cls.__module__}.{cls.__name__}" + + +class AbstractConfig(ListCachable): + objects = AbstractConfigQuerySet.as_manager() + name = models.CharField( + max_length=100, + null=False, + unique=True, + validators=[plugin_name_validator], + ) + description = models.TextField(null=False) + + disabled = models.BooleanField(null=False, default=False) + orgs_configuration = GenericRelation(OrganizationPluginConfiguration) + + class Meta: + indexes = [models.Index(fields=["name"]), models.Index(fields=["disabled"])] + abstract = True + + def __str__(self): + return self.name + + def get_or_create_org_configuration( + self, organization: Organization + ) -> OrganizationPluginConfiguration: + try: + org_configuration = self.orgs_configuration.get(organization=organization) + except OrganizationPluginConfiguration.DoesNotExist: + org_configuration = OrganizationPluginConfiguration.objects.create( + config=self, organization=organization + ) + return org_configuration + + @classmethod + def get_content_type(cls) -> ContentType: + return ContentType.objects.get( + model=cls._meta.model_name, app_label=cls._meta.app_label + ) + + @property + def disabled_in_organizations(self) -> QuerySet: + return self.orgs_configuration.filter(disabled=True) + + @classmethod + @property + def runtime_configuration_key(cls) -> str: + return f"{cls.__name__.split('Config')[0].lower()}s" + + @classmethod + @property + def snake_case_name(cls) -> str: + import re + + return re.sub(r"(? bool: + return ( + self.__class__.objects.filter(pk=self.pk) + .annotate_runnable(user) + .first() + .runnable + ) + + def enabled_for_user(self, user: User) -> bool: + if user.has_membership(): + return ( + not self.disabled + and not self.orgs_configuration.filter( + disabled=True, organization__pk=user.membership.organization_id + ).exists() + ) + return not self.disabled + + +class AbstractReport(models.Model): + objects = AbstractReportQuerySet.as_manager() + # constants + Status = ReportStatus + + # fields + status = models.CharField(max_length=50, choices=Status.choices) + report = models.JSONField(default=dict) + errors = pg_fields.ArrayField( + models.CharField(max_length=512), default=list, blank=True + ) + start_time = models.DateTimeField(default=timezone.now) + end_time = models.DateTimeField(default=timezone.now) + task_id = models.UUIDField() # tracks celery task id + + job = models.ForeignKey( + "api_app.Job", related_name="%(class)ss", on_delete=models.CASCADE + ) + parameters = models.JSONField(blank=False, null=False, editable=False) + sent_to_bi = models.BooleanField(default=False, editable=False) + + class Meta: + abstract = True + indexes = [ + models.Index( + fields=["sent_to_bi", "-start_time"], name="%(class)ssBISearch" + ) + ] + + def __str__(self): + return f"{self.__class__.__name__}(job:#{self.job_id}, {self.config.name})" + + @classmethod + @property + def config(cls) -> "AbstractConfig": + raise NotImplementedError() + + @cached_property + def runtime_configuration(self): + return self.job.get_config_runtime_configuration(self.config) + + # properties + @property + def user(self) -> models.Model: + return self.job.user + + @property + def process_time(self) -> float: + secs = (self.end_time - self.start_time).total_seconds() + return round(secs, 2) + + +class PythonConfig(AbstractConfig): + objects = PythonConfigQuerySet.as_manager() + soft_time_limit = models.IntegerField(default=60, validators=[MinValueValidator(0)]) + routing_key = models.CharField(max_length=50, default="default") + python_module = models.ForeignKey( + PythonModule, on_delete=models.PROTECT, related_name="%(class)ss" + ) + + health_check_task = models.OneToOneField( + PeriodicTask, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="healthcheck_for_%(class)s", + editable=False, + ) + health_check_status = models.BooleanField(default=True, editable=False) + + class Meta: + abstract = True + indexes = [ + models.Index(fields=("python_module", "disabled")), + ] + ordering = ["name", "disabled"] + + def get_routing_key(self) -> str: + if self.routing_key not in settings.CELERY_QUEUES: + logger.warning( + f"{self.name}: you have no worker for {self.routing_key}." + f" Using {settings.DEFAULT_QUEUE} queue." + ) + return settings.DEFAULT_QUEUE + return self.routing_key + + @property + def parameters(self) -> ParameterQuerySet: + return Parameter.objects.filter(python_module=self.python_module) + + @classmethod + @property + def report_class(cls) -> Type[AbstractReport]: + return cls.reports.rel.related_model + + @classmethod + def get_subclasses(cls) -> typing.List["PythonConfig"]: + child_models = [ct.model_class() for ct in ContentType.objects.all()] + return [ + model + for model in child_models + if (model is not None and issubclass(model, cls) and model is not cls) + ] + + @classmethod + @property + def plugin_type(cls) -> str: + # retro compatibility + + raise NotImplementedError() + + def _get_params(self, user: User, runtime_configuration: Dict) -> Dict[str, Any]: + return { + parameter.name: parameter.value + for parameter in self.read_configured_params(user, runtime_configuration) + if not parameter.is_secret + } + + def generate_empty_report(self, job: Job, task_id: str, status: str): + return self.python_module.python_class.report_model.objects.update_or_create( + job=job, + config=self, + defaults={ + "status": status, + "task_id": task_id, + "start_time": now(), + "end_time": now(), + "parameters": self._get_params( + job.user, job.get_config_runtime_configuration(self) + ), + }, + )[0] + + def refresh_cache_keys(self, user: User = None): + from api_app.serializers.plugin import PythonConfigListSerializer + + base_key = ( + f"{self.__class__.__name__}_{self.name}_{user.username if user else ''}" + ) + for key in cache.get_where(f"serializer_{base_key}").keys(): + logger.debug(f"Deleting cache key {key}") + cache.delete(key) + if user: + PythonConfigListSerializer( + child=self.serializer_class() + ).to_representation_single_plugin(self, user) + else: + for generic_user in User.objects.exclude(email=""): + PythonConfigListSerializer( + child=self.serializer_class() + ).to_representation_single_plugin(self, generic_user) + + @classmethod + @property + def serializer_class(cls) -> Type["PythonConfigSerializer"]: + raise NotImplementedError() + + @classmethod + @property + def plugin_name(cls) -> str: + return cls.__name__.split("Config")[0] + + @classmethod + def signature_pipeline_running(cls, job) -> Signature: + return cls._signature_pipeline_status( + job, getattr(Status, f"{cls.plugin_name.upper()}S_RUNNING").value + ) + + @classmethod + def signature_pipeline_completed(cls, job) -> Signature: + return cls._signature_pipeline_status( + job, getattr(Status, f"{cls.plugin_name.upper()}S_COMPLETED").value + ) + + @classmethod + def _signature_pipeline_status(cls, job, status: str) -> Signature: + return tasks.job_set_pipeline_status.signature( + args=[job.pk, status], + kwargs={}, + queue=get_queue_name(settings.CONFIG_QUEUE), + immutable=True, + MessageGroupId=str(uuid.uuid4()), + priority=job.priority, + ) + + @property + def queue(self): + return get_queue_name(self.get_routing_key()) + + @property + def options(self) -> QuerySet: + return self.parameters.filter(is_secret=False) + + @property + def secrets(self) -> QuerySet: + return self.parameters.filter(is_secret=True) + + @property + def required_parameters(self) -> QuerySet: + return self.parameters.filter(required=True) + + @deprecated("Please use the queryset method `annotate_configured`.") + def _is_configured(self, user: User = None) -> bool: + pc = self.__class__.objects.filter(pk=self.pk).annotate_configured(user).first() + return pc.configured + + @classmethod + @property + def config_exception(cls): + raise NotImplementedError() + + def read_configured_params( + self, user: User = None, config_runtime: Dict = None + ) -> ParameterQuerySet: + params = self.parameters.annotate_configured( + self, user + ).annotate_value_for_user(self, user, config_runtime) + not_configured_params = params.filter(required=True, configured=False) + # TODO to optimize + if not_configured_params.exists(): + param = not_configured_params.first() + if not settings.STAGE_CI or settings.STAGE_CI and not param.value: + raise TypeError( + f"Required param {param.name} " + f"of plugin {param.python_module.module}" + " does not have a valid value" + ) + if settings.STAGE_CI: + return params.filter(Q(configured=True) | Q(value__isnull=False)) + return params.filter(configured=True) + + def generate_health_check_periodic_task(self): + from intel_owl.tasks import health_check + + if ( + hasattr(self.python_module, "health_check_schedule") + and self.python_module.health_check_schedule + ): + periodic_task = PeriodicTask.objects.update_or_create( + name=f"{self.name.title()}HealthCheck{self.__class__.__name__}", + task=f"{health_check.__module__}.{health_check.__name__}", + defaults={ + "crontab": self.python_module.health_check_schedule, + "queue": self.queue, + "enabled": not self.disabled, + "kwargs": json.dumps( + { + "python_module_pk": self.python_module_id, + "plugin_config_pk": self.pk, + } + ), + }, + )[0] + self.health_check_task = periodic_task + self.save() diff --git a/Submodules/IntelOwl/api_app/permissions.py b/Submodules/IntelOwl/api_app/permissions.py new file mode 100644 index 0000000..9fd5c5b --- /dev/null +++ b/Submodules/IntelOwl/api_app/permissions.py @@ -0,0 +1,48 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from logging import getLogger + +from rest_framework.permissions import BasePermission + +logger = getLogger(__name__) + + +class IsObjectOwnerPermission(BasePermission): + @staticmethod + def has_object_permission(request, view, obj): + obj_owner = getattr(obj, "owner", None) + if not obj_owner: + return False + return obj_owner == request.user + + +class IsObjectSameOrgPermission(BasePermission): + @staticmethod + def has_object_permission(request, view, obj): + return ( + obj.owner.has_membership() + and request.user.has_membership() + and obj.owner.membership.organization_id + == request.user.membership.organization_id + ) + + +IsObjectOwnerOrSameOrgPermission = IsObjectSameOrgPermission | IsObjectOwnerPermission + + +class IsObjectAdminPermission(BasePermission): + @staticmethod + def has_object_permission(request, view, obj): + obj_owner = getattr(obj, "owner", None) + # if the object was not made for an organization, we return false + if not obj_owner or not obj_owner.has_membership(): + return False + else: + # if we are admin we can e have to check that is our org + return ( + request.user.has_membership() + and request.user.membership.is_admin + and obj_owner.membership.organization + == request.user.membership.organization + ) diff --git a/Submodules/IntelOwl/api_app/pivots_manager/__init__.py b/Submodules/IntelOwl/api_app/pivots_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/pivots_manager/admin.py b/Submodules/IntelOwl/api_app/pivots_manager/admin.py new file mode 100644 index 0000000..c458fc1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/admin.py @@ -0,0 +1,38 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.contrib import admin +from django.http import HttpRequest + +from api_app.admin import AbstractReportAdminView, PythonConfigAdminView +from api_app.pivots_manager.forms import PivotConfigAdminForm +from api_app.pivots_manager.models import PivotConfig, PivotMap, PivotReport + + +@admin.register(PivotReport) +class PivotReportAdminView(AbstractReportAdminView): + ... + + +@admin.register(PivotConfig) +class PivotConfigAdminView(PythonConfigAdminView): + list_display = PythonConfigAdminView.list_display + ( + "get_related_configs", + "playbook_to_execute", + ) + form = PivotConfigAdminForm + + @admin.display(description="Related Configs") + def get_related_configs(self, instance: PivotConfig): + return list(instance.related_configs.values_list("name", flat=True)) + + +@admin.register(PivotMap) +class PivotMapAdminView(admin.ModelAdmin): + list_display = ["pk", "starting_job", "pivot_config", "ending_job", "owner"] + + def has_add_permission(self, request: HttpRequest) -> bool: + return False + + def has_change_permission(self, request: HttpRequest, obj=None) -> bool: + return False diff --git a/Submodules/IntelOwl/api_app/pivots_manager/apps.py b/Submodules/IntelOwl/api_app/pivots_manager/apps.py new file mode 100644 index 0000000..e02d74f --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class PivotsManagerConfig(AppConfig): + name = "api_app.pivots_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/pivots_manager/classes.py b/Submodules/IntelOwl/api_app/pivots_manager/classes.py new file mode 100644 index 0000000..0a37671 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/classes.py @@ -0,0 +1,88 @@ +import abc +import logging +from typing import Any, Optional, Tuple, Type + +from django.db.models import QuerySet + +from api_app.choices import PythonModuleBasePaths +from api_app.classes import Plugin +from api_app.models import AbstractReport +from api_app.pivots_manager.exceptions import ( + PivotConfigurationException, + PivotRunException, +) +from api_app.pivots_manager.models import PivotConfig, PivotMap, PivotReport +from api_app.queryset import PythonConfigQuerySet + +logger = logging.getLogger(__name__) + + +class Pivot(Plugin, metaclass=abc.ABCMeta): + @classmethod + @property + def python_base_path(cls): + return PythonModuleBasePaths.Pivot.value + + @property + def related_configs(self) -> PythonConfigQuerySet: + self._config: PivotConfig + return self._config.related_configs + + @property + def related_reports(self) -> QuerySet: + report_class: Type[AbstractReport] = self.related_configs.model.report_class + return report_class.objects.filter( + config__in=self.related_configs, job=self._job + ) + + @classmethod + @property + def report_model(cls) -> Type[PivotReport]: + return PivotReport + + @classmethod + @property + def config_model(cls) -> Type[PivotConfig]: + return PivotConfig + + def should_run(self) -> Tuple[bool, Optional[str]]: + # by default, the pivot run IF every report attached to it was success + result = not self.related_reports.exclude( + status=self.report_model.Status.SUCCESS.value + ).exists() + return ( + result, + f"All necessary reports{'' if result else ' do not'} have success status", + ) + + def get_value_to_pivot_to(self) -> Any: + raise NotImplementedError() + + def before_run(self): + super().before_run() + self._config: PivotConfig + self._config.validate_playbook_to_execute(self._user) + + def run(self) -> Any: + self._config: PivotConfig + to_run, motivation = self.should_run() + report = {"create_job": to_run, "motivation": motivation, "jobs_id": []} + if to_run: + content = self.get_value_to_pivot_to() + logger.info(f"Creating jobs from {content}") + for job in self._config.create_jobs( + content, self._job.tlp, self._user, parent_job=self._job + ): + report["jobs_id"].append(job.pk) + PivotMap.objects.create( + starting_job=self._job, ending_job=job, pivot_config=self._config + ) + else: + logger.info(f"Skipping job creation for {self._config.name}") + return report + + def get_exceptions_to_catch(self) -> list: + return [ + PivotConfigurationException, + PivotRunException, + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/exceptions.py b/Submodules/IntelOwl/api_app/pivots_manager/exceptions.py new file mode 100644 index 0000000..6ca0f89 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/exceptions.py @@ -0,0 +1,18 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +class NotRunnablePivot(Exception): + pass + + +class PivotConfigurationException(Exception): + pass + + +class PivotRunException(Exception): + pass + + +class PivotFieldNotFoundException(Exception): + pass diff --git a/Submodules/IntelOwl/api_app/pivots_manager/forms.py b/Submodules/IntelOwl/api_app/pivots_manager/forms.py new file mode 100644 index 0000000..378ab97 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/forms.py @@ -0,0 +1,26 @@ +from django import forms + +from api_app.pivots_manager.models import PivotConfig + + +class PivotConfigAdminForm(forms.ModelForm): + description = forms.CharField( + disabled=True, + required=False, + initial="", + widget=forms.Textarea(), + ) + + class Meta: + model = PivotConfig + fields = [ + "name", + "description", + "routing_key", + "soft_time_limit", + "disabled", + "python_module", + "related_analyzer_configs", + "related_connector_configs", + "playbook_to_execute", + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0001_1_initial_squashed.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0001_1_initial_squashed.py new file mode 100644 index 0000000..00f753e --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0001_1_initial_squashed.py @@ -0,0 +1,266 @@ +# Generated by Django 4.2.8 on 2024-02-08 11:43 + +import django.contrib.postgres.fields +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + +# Functions from the following migrations need manual copying. +# Move them and any dependencies into this file, then update the +# RunPython operations to refer to the local versions: +# api_app.pivots_manager.migrations.0008_data_migrate +# api_app.pivots_manager.migrations.0012_alter_pivotconfig_unique_together_and_more +# api_app.pivots_manager.migrations.0014_pivotconfig_no_field_to_compare +# api_app.pivots_manager.migrations.0017_pivotconfig_routing_key_pivotconfig_soft_time_limit + + +class Migration(migrations.Migration): + initial = True + replaces = [ + # ("pivots_manager", "0001_initial"), + # ( + # "pivots_manager", + # "0002_rename_pivot_manag_startin_21e74a_idx_pivots_mana_startin_694120_idx_and_more", + # ), + # ("pivots_manager", "0003_alter_pivot_ending_job_alter_pivot_starting_job"), + # ("pivots_manager", "0004_alter_pivotconfig_analyzer_config_and_more"), + # ("pivots_manager", "0005_pivotconfig_pivot_config_no_config_all_null"), + # ("pivots_manager", "0006_alter_pivotconfig_analyzer_config_and_more"), + # ("pivots_manager", "0007_pivotreport_rename_pivot_pivotmap_and_more"), + # ("pivots_manager", "0008_data_migrate"), + # ("pivots_manager", "0009_alter_pivotconfig_python_module"), + # ("pivots_manager", "0010_alter_pivotconfig_execute_on_python_module"), + # ("pivots_manager", "0011_alter_pivotconfig_name"), + # ("pivots_manager", "0012_alter_pivotconfig_unique_together_and_more"), + # ("pivots_manager", "0013_pivotconfig_pivot_config_no_null_configs"), + # ("pivots_manager", "0014_pivotconfig_no_field_to_compare"), + # ("pivots_manager", "0015_alter_pivotmap_pivot_config"), + # ("pivots_manager", "0016_alter_pivotconfig_options_and_more"), + # ("pivots_manager", "0017_pivotconfig_routing_key_pivotconfig_soft_time_limit"), + # ("pivots_manager", "0018_pivotconfig_health_check_task"), + # ("pivots_manager", "0019_pivotconfig_health_check_status"), + # ("pivots_manager", "0020_pivotreport_parameters"), + # ("pivots_manager", "0021_pivotreport_sent_to_bi"), + # ("pivots_manager", "0022_pivotreport_pivotreportsbisearch"), + ] + + dependencies = [ + ("api_app", "0001_1_initial_squashed"), + ("analyzers_manager", "0001_initial_squashed"), + ("connectors_manager", "0001_initial_squashed"), + ] + + operations = [ + migrations.CreateModel( + name="PivotConfig", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("description", models.TextField()), + ("disabled", models.BooleanField(default=False)), + ( + "python_module", + models.ForeignKey( + limit_choices_to={"base_path": "api_app.pivots_manager.pivots"}, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)ss", + to="api_app.pythonmodule", + ), + ), + ( + "name", + models.CharField( + max_length=100, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", + "Your name should match the [A-Za-z0-9_] characters", + ) + ], + ), + ), + ( + "disabled_in_organizations", + models.ManyToManyField( + blank=True, + related_name="%(app_label)s_%(class)s_disabled", + to="certego_saas_organization.organization", + ), + ), + ( + "health_check_status", + models.BooleanField(default=True, editable=False), + ), + ( + "health_check_task", + models.OneToOneField( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="healthcheck_for_%(class)s", + to="django_celery_beat.periodictask", + ), + ), + ( + "soft_time_limit", + models.IntegerField( + default=60, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ("routing_key", models.CharField(default="default", max_length=50)), + ( + "related_connector_configs", + models.ManyToManyField( + blank=True, + related_name="pivots", + to="connectors_manager.connectorconfig", + ), + ), + ( + "related_analyzer_configs", + models.ManyToManyField( + blank=True, + related_name="pivots", + to="analyzers_manager.analyzerconfig", + ), + ), + ], + ), + migrations.AlterModelOptions( + name="pivotconfig", + options={"ordering": ["name", "disabled"]}, + ), + migrations.AddIndex( + model_name="pivotconfig", + index=models.Index( + fields=["python_module", "disabled"], + name="pivots_mana_python__75b643_idx", + ), + ), + migrations.CreateModel( + name="PivotReport", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("FAILED", "Failed"), + ("PENDING", "Pending"), + ("RUNNING", "Running"), + ("SUCCESS", "Success"), + ("KILLED", "Killed"), + ], + max_length=50, + ), + ), + ("report", models.JSONField(default=dict)), + ( + "errors", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=512), + blank=True, + default=list, + size=None, + ), + ), + ("start_time", models.DateTimeField(default=django.utils.timezone.now)), + ("end_time", models.DateTimeField(default=django.utils.timezone.now)), + ("task_id", models.UUIDField()), + ("sent_to_bi", models.BooleanField(default=False, editable=False)), + ("parameters", models.JSONField(editable=False)), + ( + "job", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_app.job", + ), + ), + ( + "config", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="pivots_manager.pivotconfig", + ), + ), + ], + ), + migrations.AlterUniqueTogether( + name="pivotreport", + unique_together={("config", "job")}, + ), + migrations.AddIndex( + model_name="pivotreport", + index=models.Index( + fields=["sent_to_bi", "-start_time"], name="pivotreportsBISearch" + ), + ), + migrations.CreateModel( + name="PivotMap", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "ending_job", + models.ForeignKey( + "api_app.Job", + on_delete=models.CASCADE, + related_name="pivot_parents", + editable=False, + ), + ), + ( + "pivot_config", + models.ForeignKey( + default=None, + editable=False, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="pivots", + to="pivots_manager.pivotconfig", + ), + ), + ( + "starting_job", + models.ForeignKey( + "api_app.Job", + on_delete=models.CASCADE, + related_name="pivot_children", + editable=False, + ), + ), + ], + ), + migrations.AlterUniqueTogether( + name="pivotmap", + unique_together={("starting_job", "pivot_config", "ending_job")}, + ), + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0001_2_initial_squashed.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0001_2_initial_squashed.py new file mode 100644 index 0000000..7d37a07 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0001_2_initial_squashed.py @@ -0,0 +1,48 @@ +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + replaces = [ + # ("pivots_manager", "0001_initial"), + # ( + # "pivots_manager", + # "0002_rename_pivot_manag_startin_21e74a_idx_pivots_mana_startin_694120_idx_and_more", + # ), + # ("pivots_manager", "0003_alter_pivot_ending_job_alter_pivot_starting_job"), + # ("pivots_manager", "0004_alter_pivotconfig_analyzer_config_and_more"), + # ("pivots_manager", "0005_pivotconfig_pivot_config_no_config_all_null"), + # ("pivots_manager", "0006_alter_pivotconfig_analyzer_config_and_more"), + # ("pivots_manager", "0007_pivotreport_rename_pivot_pivotmap_and_more"), + # ("pivots_manager", "0008_data_migrate"), + # ("pivots_manager", "0009_alter_pivotconfig_python_module"), + # ("pivots_manager", "0010_alter_pivotconfig_execute_on_python_module"), + # ("pivots_manager", "0011_alter_pivotconfig_name"), + # ("pivots_manager", "0012_alter_pivotconfig_unique_together_and_more"), + # ("pivots_manager", "0013_pivotconfig_pivot_config_no_null_configs"), + # ("pivots_manager", "0014_pivotconfig_no_field_to_compare"), + # ("pivots_manager", "0015_alter_pivotmap_pivot_config"), + # ("pivots_manager", "0016_alter_pivotconfig_options_and_more"), + # ("pivots_manager", "0017_pivotconfig_routing_key_pivotconfig_soft_time_limit"), + # ("pivots_manager", "0018_pivotconfig_health_check_task"), + # ("pivots_manager", "0019_pivotconfig_health_check_status"), + # ("pivots_manager", "0020_pivotreport_parameters"), + # ("pivots_manager", "0021_pivotreport_sent_to_bi"), + # ("pivots_manager", "0022_pivotreport_pivotreportsbisearch"), + ] + dependencies = [ + ("pivots_manager", "0001_1_initial_squashed"), + ("playbooks_manager", "0001_initial_squashed"), + ] + operations = [ + migrations.AddField( + model_name="pivotconfig", + name="playbook_to_execute", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="executed_by_pivot", + to="playbooks_manager.playbookconfig", + ), + ) + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0002_000_self_analyzable.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0002_000_self_analyzable.py new file mode 100644 index 0000000..dfa4a5d --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0002_000_self_analyzable.py @@ -0,0 +1,32 @@ +from django.db import migrations + + +def migrate_python_module_pivot(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + PythonModule.objects.update_or_create( + module="self_analyzable.SelfAnalyzable", + base_path="api_app.pivots_manager.pivots", + ) + PythonModule.objects.create( + module="base.Base", base_path="api_app.pivots_manager.pivots" + ) + + +def reverse_migrate_module_pivot(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + PythonModule.objects.get( + module="self_analyzable.SelfAnalyzable", + base_path="api_app.pivots_manager.pivots", + ).delete() + PythonModule.objects.get( + module="base.Base", base_path="api_app.pivots_manager.pivots" + ).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("pivots_manager", "0001_2_initial_squashed"), + ] + operations = [ + migrations.RunPython(migrate_python_module_pivot, reverse_migrate_module_pivot) + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0002_001_compare.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0002_001_compare.py new file mode 100644 index 0000000..0bbdf74 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0002_001_compare.py @@ -0,0 +1,40 @@ +from django.db import migrations + + +def migrate_python_module_pivot(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + pm, _ = PythonModule.objects.update_or_create( + module="compare.Compare", + base_path="api_app.pivots_manager.pivots", + ) + Parameter = apps.get_model("api_app", "Parameter") + Parameter.objects.get_or_create( + name="field_to_compare", + type="str", + python_module=pm, + is_secret=False, + required=True, + defaults={ + "description": "Dotted path to the field", + }, + ) + + +def reverse_migrate_module_pivot(apps, schema_editor): + PythonModule = apps.get_model("api_app", "PythonModule") + PythonModule.objects.get( + module="self_analyzable.SelfAnalyzable", + base_path="api_app.pivots_manager.pivots", + ).delete() + PythonModule.objects.get( + module="base.Base", base_path="api_app.pivots_manager.pivots" + ).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("pivots_manager", "0002_000_self_analyzable"), + ] + operations = [ + migrations.RunPython(migrate_python_module_pivot, reverse_migrate_module_pivot) + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0023_2_change_primary_key.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0023_2_change_primary_key.py new file mode 100644 index 0000000..c91470b --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0023_2_change_primary_key.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.contrib.postgres.fields +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PivotConfig = apps.get_model("pivots_manager", "PivotConfig") + PivotConfig.objects.update( + related_analyzer_configs2=ArraySubquery( + PivotConfig.objects.filter(pk=models.OuterRef("pk")).values( + "related_analyzer_configs__name" + ) + ), + related_connector_configs2=ArraySubquery( + PivotConfig.objects.filter(pk=models.OuterRef("pk")).values( + "related_connector_configs__name" + ) + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_1_change_primary_key"), + ("connectors_manager", "0029_1_change_primary_key"), + ("pivots_manager", "0002_001_compare"), + ] + + operations = [ + migrations.AddField( + model_name="pivotconfig", + name="related_analyzer_configs2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="pivotconfig", + name="related_connector_configs2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate), + migrations.RemoveField( + model_name="pivotconfig", + name="related_analyzer_configs", + ), + migrations.RemoveField( + model_name="pivotconfig", + name="related_connector_configs", + ), + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0023_4_change_primary_key.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0023_4_change_primary_key.py new file mode 100644 index 0000000..e66fce4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0023_4_change_primary_key.py @@ -0,0 +1,55 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PivotConfig = apps.get_model("pivots_manager", "PivotConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + ConnectorConfig = apps.get_model("connectors_manager", "ConnectorConfig") + for pivot in PivotConfig.objects.all(): + acs = AnalyzerConfig.objects.filter( + name__in=pivot.related_analyzer_configs2 + ).values_list("pk", flat=True) + ccs = ConnectorConfig.objects.filter( + name__in=pivot.related_connector_configs2 + ).values_list("pk", flat=True) + pivot.related_analyzer_configs.set(acs) + pivot.related_connector_configs.set(ccs) + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_3_change_primary_key"), + ("connectors_manager", "0029_3_change_primary_key"), + ("pivots_manager", "0023_2_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="pivotconfig", + name="related_analyzer_configs", + field=models.ManyToManyField( + blank=True, + related_name="pivots", + to="analyzers_manager.AnalyzerConfig", + ), + ), + migrations.AddField( + model_name="pivotconfig", + name="related_connector_configs", + field=models.ManyToManyField( + blank=True, + related_name="pivots", + to="connectors_manager.ConnectorConfig", + ), + ), + migrations.RunPython( + migrate, + ), + migrations.RemoveField( + model_name="pivotconfig", name="related_analyzer_configs2" + ), + migrations.RemoveField( + model_name="pivotconfig", name="related_connector_configs2" + ), + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0024_2_change_primary_key.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0024_2_change_primary_key.py new file mode 100644 index 0000000..9a326a2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0024_2_change_primary_key.py @@ -0,0 +1,46 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PivotConfig = apps.get_model("pivots_manager", "PivotConfig") + PivotConfig.objects.update( + disabled2=ArraySubquery( + PivotConfig.objects.filter(pk=models.OuterRef("pk")).values( + "disabled_in_organizations__pk" + ) + ) + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_1_change_primary_key"), + ("pivots_manager", "0023_4_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="pivotconfig", + name="disabled2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.IntegerField(), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython( + migrate, + ), + migrations.AlterField( + model_name="pivotconfig", + name="playbook_to_execute", + field=models.CharField(max_length=100, null=True, blank=True), + ), + migrations.RemoveField( + model_name="pivotconfig", name="disabled_in_organizations" + ), + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0024_4_change_primary_key.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0024_4_change_primary_key.py new file mode 100644 index 0000000..a80ce60 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0024_4_change_primary_key.py @@ -0,0 +1,78 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PivotConfig = apps.get_model("pivots_manager", "PivotConfig") + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + Organization = apps.get_model("certego_saas_organization", "Organization") + PivotConfig.objects.update( + playbook_to_execute2=models.Subquery( + PlaybookConfig.objects.filter( + name=models.OuterRef("playbook_to_execute") + ).values_list("pk")[:1] + ), + ) + for config in PivotConfig.objects.all(): + if config.disabled2: + OrganizationPluginConfiguration = apps.get_model( + "api_app", "OrganizationPluginConfiguration" + ) + ContentType = apps.get_model("contenttypes", "ContentType") + ct = ContentType.objects.get_for_model(config) + for org in config.disabled2: + if org: + OrganizationPluginConfiguration.objects.create( + organization=Organization.objects.get(pk=org), + object_id=config.pk, + content_type=ct, + disabled=True, + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("playbooks_manager", "0024_3_change_primary_key"), + ("pivots_manager", "0024_2_change_primary_key"), + ] + + operations = [ + migrations.AlterField( + model_name="pivotconfig", + name="playbook_to_execute", + field=models.CharField(max_length=100, null=True, blank=True), + ), + migrations.AddField( + model_name="pivotconfig", + name="playbook_to_execute2", + field=models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.PROTECT, + related_name="executed_by_pivot", + to="playbooks_manager.playbookconfig", + null=True, + ), + preserve_default=False, + ), + migrations.RunPython( + migrate, + ), + migrations.RemoveField(model_name="pivotconfig", name="playbook_to_execute"), + migrations.RenameField( + model_name="pivotconfig", + old_name="playbook_to_execute2", + new_name="playbook_to_execute", + ), + migrations.AlterField( + model_name="pivotconfig", + name="playbook_to_execute", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="executed_by_pivot", + to="playbooks_manager.playbookconfig", + ), + ), + migrations.RemoveField(model_name="pivotconfig", name="disabled2"), + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0025_alter_pivotmap_ending_job.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0025_alter_pivotmap_ending_job.py new file mode 100644 index 0000000..6c3cdab --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0025_alter_pivotmap_ending_job.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.8 on 2024-02-01 16:04 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0058_4_change_primary_key"), + ("pivots_manager", "0024_4_change_primary_key"), + ] + + operations = [ + migrations.AlterField( + model_name="pivotmap", + name="ending_job", + field=models.OneToOneField( + editable=False, + on_delete=django.db.models.deletion.CASCADE, + related_name="pivot_parent", + to="api_app.job", + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0026_pivot_config_abuseiptosubmission.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0026_pivot_config_abuseiptosubmission.py new file mode 100644 index 0000000..a033750 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0026_pivot_config_abuseiptosubmission.py @@ -0,0 +1,150 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "compare.Compare", + "base_path": "api_app.pivots_manager.pivots", + }, + "related_analyzer_configs": ["Abusix"], + "related_connector_configs": [], + "playbook_to_execute": "Send_Abuse_Email", + "name": "AbuseIpToSubmission", + "description": "This Plugin leverages results from the Abusix analyzer to " + "extract the abuse contacts of an IP address to pivot to " + "the AbuseSubmitter connector.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "model": "pivots_manager.PivotConfig", +} + +params = [ + { + "python_module": { + "module": "compare.Compare", + "base_path": "api_app.pivots_manager.pivots", + }, + "name": "field_to_compare", + "type": "str", + "description": "Dotted path to the field", + "is_secret": False, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "compare.Compare", + "base_path": "api_app.pivots_manager.pivots", + }, + "name": "field_to_compare", + "type": "str", + "description": "Dotted path to the field", + "is_secret": False, + "required": True, + }, + "analyzer_config": None, + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": "AbuseIpToSubmission", + "for_organization": False, + "value": "abuse_contacts.0", + "updated_at": "2024-04-22T14:08:49.711495Z", + "owner": None, + }, +] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("pivots_manager", "0025_alter_pivotmap_ending_job"), + ("playbooks_manager", "0033_playbook_config_send_abuse_email"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/0027_pivot_config_takedownrequesttoabuseip.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0027_pivot_config_takedownrequesttoabuseip.py new file mode 100644 index 0000000..0fb56ec --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/migrations/0027_pivot_config_takedownrequesttoabuseip.py @@ -0,0 +1,155 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "python_module": { + "health_check_schedule": None, + "update_schedule": None, + "module": "any_compare.AnyCompare", + "base_path": "api_app.pivots_manager.pivots", + }, + "related_analyzer_configs": [ + "Classic_DNS", + "CloudFlare_DNS", + "DNS0_EU", + "Google_DNS", + "Quad9_DNS", + ], + "related_connector_configs": [], + "playbook_to_execute": "Abuse_IP", + "name": "TakedownRequestToAbuseIp", + "description": "This Plugin leverages results from DNS resolver analyzers " + "to extract a valid IP address to pivot to the Abusix analyzer.", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "model": "pivots_manager.PivotConfig", +} + +params = [ + { + "python_module": { + "module": "any_compare.AnyCompare", + "base_path": "api_app.pivots_manager.pivots", + }, + "name": "field_to_compare", + "type": "str", + "description": "Dotted path to the field", + "is_secret": False, + "required": True, + }, +] + +values = [ + { + "parameter": { + "python_module": { + "module": "any_compare.AnyCompare", + "base_path": "api_app.pivots_manager.pivots", + }, + "name": "field_to_compare", + "type": "str", + "description": "Dotted path to the field", + "is_secret": False, + "required": True, + }, + "analyzer_config": None, + "connector_config": None, + "visualizer_config": None, + "ingestor_config": None, + "pivot_config": "TakedownRequestToAbuseIp", + "for_organization": False, + "value": "resolutions.0.data", + "updated_at": "2024-04-22T14:08:49.711495Z", + "owner": None, + }, +] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("pivots_manager", "0026_pivot_config_abuseiptosubmission"), + ("playbooks_manager", "0034_playbook_config_abuse_ip"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/pivots_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/pivots_manager/models.py b/Submodules/IntelOwl/api_app/pivots_manager/models.py new file mode 100644 index 0000000..f08037d --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/models.py @@ -0,0 +1,136 @@ +import logging +import typing +from typing import Type + +from django.contrib.contenttypes.fields import GenericRelation + +from api_app.pivots_manager.queryset import PivotConfigQuerySet, PivotReportQuerySet +from api_app.queryset import PythonConfigQuerySet + +if typing.TYPE_CHECKING: + from api_app.serializers.plugin import PythonConfigSerializer + +from django.db import models +from django.utils.functional import cached_property + +from api_app.choices import PythonModuleBasePaths +from api_app.interfaces import CreateJobsFromPlaybookInterface +from api_app.models import AbstractReport, Job, PythonConfig, PythonModule +from api_app.pivots_manager.exceptions import PivotConfigurationException + +logger = logging.getLogger(__name__) + + +class PivotReport(AbstractReport): + objects = PivotReportQuerySet.as_manager() + config = models.ForeignKey( + "PivotConfig", related_name="reports", null=False, on_delete=models.CASCADE + ) + + class Meta: + unique_together = [("config", "job")] + indexes = AbstractReport.Meta.indexes + + +class PivotMap(models.Model): + starting_job = models.ForeignKey( + Job, + on_delete=models.CASCADE, + related_name="pivot_children", + editable=False, + ) + pivot_config = models.ForeignKey( + "PivotConfig", + on_delete=models.PROTECT, + related_name="pivots", + editable=False, + default=None, + null=True, + ) + ending_job = models.OneToOneField( + Job, + on_delete=models.CASCADE, + related_name="pivot_parent", + editable=False, + ) + + class Meta: + unique_together = [ + ("starting_job", "pivot_config", "ending_job"), + ] + + def __str__(self): + return f"Job {self.starting_job_id} -> Job {self.ending_job_id}" + + @cached_property + def report(self) -> typing.Optional[AbstractReport]: + if self.pivot_config: + return self.pivot_config.reports.get(job=self.starting_job) + return None + + @cached_property + def owner(self) -> str: + return self.starting_job.user.username + + +class PivotConfig(PythonConfig, CreateJobsFromPlaybookInterface): + objects = PivotConfigQuerySet.as_manager() + python_module = models.ForeignKey( + PythonModule, + on_delete=models.PROTECT, + related_name="%(class)ss", + limit_choices_to={"base_path": PythonModuleBasePaths.Pivot.value}, + ) + + related_analyzer_configs = models.ManyToManyField( + "analyzers_manager.AnalyzerConfig", related_name="pivots", blank=True + ) + related_connector_configs = models.ManyToManyField( + "connectors_manager.ConnectorConfig", related_name="pivots", blank=True + ) + playbook_to_execute = models.ForeignKey( + "playbooks_manager.PlaybookConfig", + on_delete=models.PROTECT, + related_name="executed_by_pivot", + null=False, + blank=False, + ) + orgs_configuration = GenericRelation( + "api_app.OrganizationPluginConfiguration", related_name="%(class)s" + ) + + def _generate_full_description(self) -> str: + plugins_name = ", ".join( + self.related_configs.all().values_list("name", flat=True) + ) + return ( + f"Pivot for plugins {plugins_name}" + " that executes" + f" playbook {self.playbook_to_execute.name}" + ) + + @property + def related_configs(self) -> PythonConfigQuerySet: + return ( + self.related_analyzer_configs.all() or self.related_connector_configs.all() + ) + + @classmethod + @property + def plugin_type(cls) -> str: + return "5" + + @classmethod + @property + def serializer_class(cls) -> Type["PythonConfigSerializer"]: + from api_app.pivots_manager.serializers import PivotConfigSerializer + + return PivotConfigSerializer + + @classmethod + @property + def config_exception(cls): + return PivotConfigurationException + + def clean(self): + super().clean() diff --git a/Submodules/IntelOwl/api_app/pivots_manager/permissions.py b/Submodules/IntelOwl/api_app/pivots_manager/permissions.py new file mode 100644 index 0000000..78c2d25 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/permissions.py @@ -0,0 +1,10 @@ +from rest_framework.permissions import BasePermission + + +class PivotOwnerPermission(BasePermission): + @staticmethod + def has_object_permission(request, view, obj): + return ( + obj.starting_job.user.pk == request.user.pk + and obj.ending_job.user.pk == request.user.pk + ) diff --git a/Submodules/IntelOwl/api_app/pivots_manager/pivots/__init__.py b/Submodules/IntelOwl/api_app/pivots_manager/pivots/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/pivots_manager/pivots/any_compare.py b/Submodules/IntelOwl/api_app/pivots_manager/pivots/any_compare.py new file mode 100644 index 0000000..c0a23ff --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/pivots/any_compare.py @@ -0,0 +1,24 @@ +import logging +from typing import Optional, Tuple + +from api_app.pivots_manager.pivots.compare import Compare + +logger = logging.getLogger(__name__) + + +class AnyCompare(Compare): + def should_run(self) -> Tuple[bool, Optional[str]]: + if result := self.related_reports.filter( + status=self.report_model.Status.SUCCESS.value + ).first(): + try: + self._value = self._get_value(self.field_to_compare) + except (RuntimeError, ValueError) as e: + return False, str(e) + return ( + bool(result), + f"All necessary reports{'' if result else ' do not'} have success status", + ) + + def update(self) -> bool: + pass diff --git a/Submodules/IntelOwl/api_app/pivots_manager/pivots/compare.py b/Submodules/IntelOwl/api_app/pivots_manager/pivots/compare.py new file mode 100644 index 0000000..5df8813 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/pivots/compare.py @@ -0,0 +1,48 @@ +from typing import Any, Optional, Tuple + +from api_app.pivots_manager.classes import Pivot + + +class Compare(Pivot): + field_to_compare: str + + @classmethod + def update(cls) -> bool: + pass + + def _get_value(self, field: str) -> Any: + report = self.related_reports.filter( + status=self.report_model.Status.SUCCESS.value + ).first() + if not report: + raise RuntimeError("No report found") + content = report.report + + for key in field.split("."): + try: + content = content[key] + except TypeError: + if isinstance(content, list) and len(content) > 0: + content = content[int(key)] + else: + raise RuntimeError(f"Not found {field}") + + if isinstance(content, (int, dict)): + raise ValueError(f"You can't use a {type(content)} as pivot") + return content + + def should_run(self) -> Tuple[bool, Optional[str]]: + if self.related_reports.count() != 1: + return ( + False, + f"Unable to run pivot {self._config.name} " + "because attached to more than one configuration", + ) + try: + self._value = self._get_value(self.field_to_compare) + except (RuntimeError, ValueError) as e: + return False, str(e) + return super().should_run() + + def get_value_to_pivot_to(self) -> Any: + return self._value diff --git a/Submodules/IntelOwl/api_app/pivots_manager/pivots/self_analyzable.py b/Submodules/IntelOwl/api_app/pivots_manager/pivots/self_analyzable.py new file mode 100644 index 0000000..37ca23c --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/pivots/self_analyzable.py @@ -0,0 +1,33 @@ +from typing import Any, Optional, Tuple + +from django.core.files import File + +from api_app.pivots_manager.classes import Pivot +from api_app.pivots_manager.models import PivotConfig + + +class SelfAnalyzable(Pivot): + def should_run(self) -> Tuple[bool, Optional[str]]: + self._config: PivotConfig + # if the pivot is executed, we should check to not have an infinite loop. + # meaning that the playbook that we will run does not have + # all the analyzers that are required to run the pivot again + to_run, motivation = super().should_run() + if to_run: + related_config_class = self.related_configs.model + related_configs_pk = set(self.related_configs.values_list("pk", flat=True)) + # the configs that the playbook execute that could match + playbook_configs = set( + related_config_class.objects.filter( + playbooks=self._config.playbook_to_execute + ).values_list("pk", flat=True) + ) + if related_configs_pk.issubset(playbook_configs): + return False, f"Found infinite loop in {self._config.name}." + return to_run, motivation + + def get_value_to_pivot_to(self) -> Any: + if self._job.is_sample: + return File(self._job.analyzed_object, name=self._job.analyzed_object_name) + else: + return self._job.analyzed_object_name diff --git a/Submodules/IntelOwl/api_app/pivots_manager/queryset.py b/Submodules/IntelOwl/api_app/pivots_manager/queryset.py new file mode 100644 index 0000000..9ccf9ad --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/queryset.py @@ -0,0 +1,42 @@ +from typing import TYPE_CHECKING, Type + +from django.db.models import Q + +from api_app.queryset import AbstractReportQuerySet, PythonConfigQuerySet + +if TYPE_CHECKING: + from api_app.pivots_manager.serializers import PivotReportBISerializer + + +class PivotConfigQuerySet(PythonConfigQuerySet): + def valid( + self, analyzers: PythonConfigQuerySet, connectors: PythonConfigQuerySet + ) -> "PivotConfigQuerySet": + qs = self + if analyzers.exists(): + qs = qs.many_to_many_to_array("related_analyzer_configs").filter( + Q( + related_analyzer_configs_array__contained_by=list( + analyzers.values_list("pk", flat=True) + ) + ) + | Q(related_analyzer_configs=None) + ) + if connectors.exists(): + qs = qs.many_to_many_to_array("related_connector_configs").filter( + Q( + related_connector_configs_array__contained_by=list( + connectors.values_list("pk", flat=True) + ) + ) + | Q(related_connector_configs=None) + ) + return qs.distinct() + + +class PivotReportQuerySet(AbstractReportQuerySet): + @classmethod + def _get_bi_serializer_class(cls) -> Type["PivotReportBISerializer"]: + from api_app.pivots_manager.serializers import PivotReportBISerializer + + return PivotReportBISerializer diff --git a/Submodules/IntelOwl/api_app/pivots_manager/serializers.py b/Submodules/IntelOwl/api_app/pivots_manager/serializers.py new file mode 100644 index 0000000..d65ac30 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/serializers.py @@ -0,0 +1,81 @@ +from rest_framework import serializers as rfs +from rest_framework.exceptions import ValidationError + +from api_app.models import Job +from api_app.pivots_manager.models import PivotConfig, PivotMap, PivotReport +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.serializers.plugin import ( + PythonConfigSerializer, + PythonConfigSerializerForMigration, +) +from api_app.serializers.report import ( + AbstractReportBISerializer, + AbstractReportSerializer, +) + + +class PivotReportSerializer(AbstractReportSerializer): + class Meta: + model = PivotReport + fields = AbstractReportSerializer.Meta.fields + list_serializer_class = AbstractReportSerializer.Meta.list_serializer_class + + +class PivotReportBISerializer(AbstractReportBISerializer): + class Meta: + model = PivotReport + fields = AbstractReportBISerializer.Meta.fields + list_serializer_class = AbstractReportBISerializer.Meta.list_serializer_class + + +class PivotMapSerializer(rfs.ModelSerializer): + starting_job = rfs.PrimaryKeyRelatedField(queryset=Job.objects.all(), required=True) + pivot_config = rfs.PrimaryKeyRelatedField( + queryset=PivotConfig.objects.all(), required=True + ) + ending_job = rfs.PrimaryKeyRelatedField(queryset=Job.objects.all(), required=True) + + class Meta: + model = PivotMap + fields = rfs.ALL_FIELDS + + def validate(self, attrs): + result = super().validate(attrs) + + if ( + result["starting_job"].user.pk != self.context["request"].user.pk + or result["ending_job"].user.pk != self.context["request"].user.pk + ): + raise ValidationError( + {"detail": "You do not have permission to pivot these two jobs"} + ) + return result + + +class PivotConfigSerializer(PythonConfigSerializer): + playbook_to_execute = rfs.SlugRelatedField( + queryset=PlaybookConfig.objects.all(), slug_field="name" + ) + + name = rfs.CharField(read_only=True) + description = rfs.CharField(read_only=True) + related_configs = rfs.SlugRelatedField(read_only=True, many=True, slug_field="name") + + class Meta: + model = PivotConfig + exclude = ["related_analyzer_configs", "related_connector_configs"] + list_serializer_class = PythonConfigSerializer.Meta.list_serializer_class + + +class PivotConfigSerializerForMigration(PythonConfigSerializerForMigration): + related_analyzer_configs = rfs.SlugRelatedField( + read_only=True, many=True, slug_field="name" + ) + related_connector_configs = rfs.SlugRelatedField( + read_only=True, many=True, slug_field="name" + ) + playbook_to_execute = rfs.SlugRelatedField(read_only=True, slug_field="name") + + class Meta: + model = PivotConfig + exclude = PythonConfigSerializerForMigration.Meta.exclude diff --git a/Submodules/IntelOwl/api_app/pivots_manager/signals.py b/Submodules/IntelOwl/api_app/pivots_manager/signals.py new file mode 100644 index 0000000..5824c70 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/signals.py @@ -0,0 +1,93 @@ +import logging +import uuid + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db.models.signals import m2m_changed, pre_save +from django.dispatch import receiver + +from api_app.pivots_manager.models import PivotConfig +from api_app.signals import migrate_finished +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +@receiver(migrate_finished) +def post_migrate_pivots_manager( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from intel_owl.tasks import refresh_cache + + refresh_cache.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=3, + args=[PivotConfig.python_path], + ) + + +@receiver(pre_save, sender=PivotConfig) +def pre_save_pivot_config( + sender, instance: PivotConfig, raw, using, update_fields, *args, **kwargs +): + try: + if instance.pk: + instance.description = instance._generate_full_description() + else: + instance.description = ( + f"Pivot that executes playbook {instance.playbook_to_execute.name}" + ) + except AttributeError: + # this happens when + # an integrity error will be raised because some fields are missing + pass + return instance + + +@receiver(m2m_changed, sender=PivotConfig.related_analyzer_configs.through) +def m2m_changed_pivot_config_analyzer_config( + sender, + instance: PivotConfig, + action: str, + reverse, + model, + pk_set, + using, + *args, + **kwargs, +): + if action == "pre_add" and instance.related_connector_configs.exists(): + raise ValidationError( + "You can't set both analyzers and connectors configs to a pivot" + ) + if action.startswith("post"): + instance.description = instance._generate_full_description() + instance.save() + + +@receiver(m2m_changed, sender=PivotConfig.related_connector_configs.through) +def m2m_changed_pivot_config_connector_config( + sender, + instance: PivotConfig, + action: str, + reverse, + model, + pk_set, + using, + *args, + **kwargs, +): + if action == "pre_add" and instance.related_analyzer_configs.exists(): + raise ValidationError( + "You can't set both analyzers and connectors configs to a pivot" + ) + if action.startswith("post"): + instance.description = instance._generate_full_description() + instance.save() diff --git a/Submodules/IntelOwl/api_app/pivots_manager/urls.py b/Submodules/IntelOwl/api_app/pivots_manager/urls.py new file mode 100644 index 0000000..d4bb935 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/urls.py @@ -0,0 +1,16 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +# Routers provide an easy way of automatically determining the URL conf. +from api_app.pivots_manager.views import PivotConfigViewSet, PivotMapViewSet + +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"pivot", PivotConfigViewSet, basename="pivot") +router.register(r"pivot_map", PivotMapViewSet, basename="pivot_map") + +urlpatterns = [ + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/pivots_manager/validators.py b/Submodules/IntelOwl/api_app/pivots_manager/validators.py new file mode 100644 index 0000000..45c9b0e --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/validators.py @@ -0,0 +1,5 @@ +from django.core.validators import RegexValidator + +pivot_regex_validator = RegexValidator( + r"^\w+(\.\w+)*$", message="Object should be a python path" +) diff --git a/Submodules/IntelOwl/api_app/pivots_manager/views.py b/Submodules/IntelOwl/api_app/pivots_manager/views.py new file mode 100644 index 0000000..25fb049 --- /dev/null +++ b/Submodules/IntelOwl/api_app/pivots_manager/views.py @@ -0,0 +1,25 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAuthenticated + +from api_app.pivots_manager.models import PivotMap, PivotReport +from api_app.pivots_manager.permissions import PivotOwnerPermission +from api_app.pivots_manager.serializers import PivotConfigSerializer, PivotMapSerializer +from api_app.views import PythonConfigViewSet, PythonReportActionViewSet + + +class PivotConfigViewSet(PythonConfigViewSet): + serializer_class = PivotConfigSerializer + + +class PivotActionViewSet(PythonReportActionViewSet): + @classmethod + @property + def report_model(cls): + return PivotReport + + +class PivotMapViewSet(viewsets.ReadOnlyModelViewSet): + permission_classes = [IsAuthenticated, PivotOwnerPermission] + serializer_class = PivotMapSerializer + lookup_field = "pk" + queryset = PivotMap.objects.all() diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/__init__.py b/Submodules/IntelOwl/api_app/playbooks_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/admin.py b/Submodules/IntelOwl/api_app/playbooks_manager/admin.py new file mode 100644 index 0000000..64f3a2d --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/admin.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.contrib import admin, messages +from django.core.exceptions import ValidationError +from django.http import HttpResponse, HttpResponseRedirect + +from api_app.admin import AbstractConfigAdminView, ModelWithOwnershipAdminView +from api_app.choices import ScanMode +from api_app.playbooks_manager.models import PlaybookConfig + + +@admin.register(PlaybookConfig) +class PlaybookConfigAdminView(AbstractConfigAdminView, ModelWithOwnershipAdminView): + list_display = ( + "name", + "type", + "disabled", + "get_analyzers", + "get_connectors", + "get_pivots", + "get_visualizers", + "scan_mode", + "starting", + ) + ModelWithOwnershipAdminView.list_display + list_filter = ( + AbstractConfigAdminView.list_filter + + ("starting",) + + ModelWithOwnershipAdminView.list_filter + ) + + @staticmethod + def _get_plugins(qs): + return [elem.name for elem in qs] + + @admin.display(description="Analyzers") + def get_analyzers(self, obj: PlaybookConfig): + return self._get_plugins(obj.analyzers.all()) + + @admin.display(description="Connectors") + def get_connectors(self, obj: PlaybookConfig): + return self._get_plugins(obj.connectors.all()) + + @admin.display(description="Pivots") + def get_pivots(self, obj: PlaybookConfig): + return self._get_plugins(obj.pivots.all()) + + @admin.display(description="Visualizers") + def get_visualizers(self, obj: PlaybookConfig): + return self._get_plugins(obj.visualizers.all()) + + @staticmethod + def scan_mode(obj: PlaybookConfig) -> str: + return ScanMode(obj.scan_mode).name + + def change_view(self, request, *args, **kwargs) -> HttpResponse: + try: + return super().change_view(request, *args, **kwargs) + except ValidationError as e: + self.message_user(request, str(e), level=messages.ERROR) + return HttpResponseRedirect(request.path) diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/apps.py b/Submodules/IntelOwl/api_app/playbooks_manager/apps.py new file mode 100644 index 0000000..971a335 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class PlaybooksManagerConfig(AppConfig): + name = "api_app.playbooks_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/fields.py b/Submodules/IntelOwl/api_app/playbooks_manager/fields.py new file mode 100644 index 0000000..9598100 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/fields.py @@ -0,0 +1,19 @@ +from django.utils.duration import _get_duration_components +from rest_framework.fields import DurationField + + +def duration_string(duration): + """Version of str(timedelta) which is not English specific.""" + days, hours, minutes, seconds, microseconds = _get_duration_components(duration) + + string = "{}:{:02d}:{:02d}:{:02d}".format(days, hours, minutes, seconds) + if microseconds: + string += ".{:06d}".format(microseconds) + + return string + + +class DayDurationField(DurationField): + @staticmethod + def to_representation(value): + return duration_string(value) diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0001_initial_squashed.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0001_initial_squashed.py new file mode 100644 index 0000000..0ef6784 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0001_initial_squashed.py @@ -0,0 +1,164 @@ +# Generated by Django 4.2.8 on 2024-02-08 13:40 + +import datetime + +import django.core.validators +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + +import api_app.defaults +import api_app.fields +import api_app.validators + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("api_app", "0001_1_initial_squashed"), + ("analyzers_manager", "0001_initial_squashed"), + ("connectors_manager", "0001_initial_squashed"), + ("pivots_manager", "0001_1_initial_squashed"), + ] + + operations = [ + migrations.CreateModel( + name="PlaybookConfig", + fields=[ + ( + "name", + models.CharField( + max_length=100, + primary_key=True, + serialize=False, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", + "Your name should match the [A-Za-z0-9_] characters", + ) + ], + ), + ), + ( + "type", + api_app.fields.ChoiceArrayField( + base_field=models.CharField( + choices=[ + ("ip", "Ip"), + ("url", "Url"), + ("domain", "Domain"), + ("hash", "Hash"), + ("generic", "Generic"), + ("file", "File"), + ], + max_length=50, + ), + size=None, + ), + ), + ("description", models.TextField()), + ("disabled", models.BooleanField(default=False)), + ( + "runtime_configuration", + models.JSONField( + blank=True, + default=api_app.defaults.default_runtime, + validators=[api_app.validators.validate_runtime_configuration], + ), + ), + ( + "analyzers", + models.ManyToManyField( + blank=True, + related_name="playbooks", + to="analyzers_manager.analyzerconfig", + ), + ), + ( + "connectors", + models.ManyToManyField( + blank=True, + related_name="playbooks", + to="connectors_manager.connectorconfig", + ), + ), + ( + "owner", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ("for_organization", models.BooleanField(default=False)), + ( + "tlp", + models.CharField( + choices=[ + ("CLEAR", "Clear"), + ("GREEN", "Green"), + ("AMBER", "Amber"), + ("RED", "Red"), + ], + max_length=8, + ), + ), + ( + "tags", + models.ManyToManyField( + blank=True, related_name="playbooks", to="api_app.tag" + ), + ), + ( + "scan_mode", + models.IntegerField( + choices=[ + (1, "Force New Analysis"), + (2, "Check Previous Analysis"), + ], + default=2, + ), + ), + ( + "scan_check_time", + models.DurationField( + blank=True, default=datetime.timedelta(days=1), null=True + ), + ), + ( + "pivots", + models.ManyToManyField( + blank=True, + related_name="used_by_playbooks", + to="pivots_manager.pivotconfig", + ), + ), + ( + "disabled_in_organizations", + models.ManyToManyField( + blank=True, + related_name="%(app_label)s_%(class)s_disabled", + to="certego_saas_organization.organization", + ), + ), + ], + ), + migrations.AlterModelOptions( + name="playbookconfig", + options={"ordering": ["name", "disabled"]}, + ), + migrations.AddIndex( + model_name="playbookconfig", + index=models.Index( + fields=["owner", "for_organization"], + name="playbooks_m_owner_i_88fb49_idx", + ), + ), + migrations.AlterUniqueTogether( + name="playbookconfig", + unique_together={("name", "owner")}, + ), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0000_playbook_config_dns.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0000_playbook_config_dns.py new file mode 100644 index 0000000..7ce5a6f --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0000_playbook_config_dns.py @@ -0,0 +1,116 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Dns", + "analyzers": [ + "Classic_DNS", + "CloudFlare_DNS", + "CloudFlare_Malicious_Detector", + "DNS0_EU", + "DNS0_EU_Malicious_Detector", + "Google_DNS", + "Quad9_DNS", + "Quad9_Malicious_Detector", + ], + "connectors": [], + "pivots": [], + "for_organization": False, + "description": "Retrieve information from DNS about the domain", + "disabled": False, + "type": ["domain"], + "runtime_configuration": { + "pivots": {}, + "analyzers": {}, + "connectors": {}, + "visualizers": {}, + }, + "scan_mode": 2, + "scan_check_time": "1 00:00:00", + "tlp": "AMBER", + "owner": None, + "disabled_in_organizations": [], + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("playbooks_manager", "0001_initial_squashed"), + ("pivots_manager", "0001_2_initial_squashed"), + ("analyzers_manager", "0002_0146_analyzer_config_zoomeye"), + ("connectors_manager", "0002_0003_connector_config_yeti"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0001_playbook_config_free_to_use_analyzers.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0001_playbook_config_free_to_use_analyzers.py new file mode 100644 index 0000000..7f4cf60 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0001_playbook_config_free_to_use_analyzers.py @@ -0,0 +1,317 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "FREE_TO_USE_ANALYZERS", + "analyzers": [ + "APKiD", + "BoxJS", + "CRXcavator", + "Capa_Info", + "Capa_Info_Shellcode", + "CheckDMARC", + "ClamAV", + "Classic_DNS", + "CloudFlare_DNS", + "CloudFlare_Malicious_Detector", + "CyberChef", + "Cymru_Hash_Registry_Get_File", + "Cymru_Hash_Registry_Get_Observable", + "DNS0_EU", + "DNS0_EU_Malicious_Detector", + "DNS0_names", + "DNS0_rrsets_name", + "DNStwist", + "Doc_Info", + "ELF_Info", + "FileScan_Search", + "FileScan_Upload_File", + "File_Info", + "FireHol_IPList", + "Floss", + "Google_DNS", + "HashLookupServer_Get_File", + "HashLookupServer_Get_Observable", + "IPApi", + "MalwareBazaar_Get_File", + "MalwareBazaar_Get_Observable", + "MalwareBazaar_Google_Observable", + "Mnemonic_PassiveDNS", + "Onionscan", + "PDF_Info", + "PE_Info", + "PEframe_Scan", + "Phishstats", + "Qiling_Linux", + "Qiling_Linux_Shellcode", + "Qiling_Windows", + "Qiling_Windows_Shellcode", + "Quad9_DNS", + "Quad9_Malicious_Detector", + "Quark_Engine", + "Robtex", + "Rtf_Info", + "Signature_Info", + "SpeakEasy", + "SpeakEasy_Shellcode", + "Stratosphere_Blacklist", + "Strings_Info", + "Suricata", + "TalosReputation", + "ThreatFox", + "Thug_HTML_Info", + "Thug_URL_Info", + "TorProject", + "Tranco", + "URLhaus", + "WhoIs_RipeDB_Search", + "Xlm_Macro_Deobfuscator", + "YARAify_Generics", + "Yara", + ], + "connectors": [], + "pivots": [], + "for_organization": False, + "description": "A playbook containing all free to use analyzers.", + "disabled": False, + "type": ["ip", "url", "domain", "generic", "hash", "file"], + "runtime_configuration": { + "analyzers": { + "Yara": { + "ignore": [ + "generic_anomalies.yar", + "general_cloaking.yar", + "thor_inverse_matches.yar", + "yara_mixed_ext_vars.yar", + "thor-webshells.yar", + ], + "repositories": [ + "https://github.com/elastic/protections-artifacts", + "https://github.com/embee-research/Yara", + "https://github.com/elceef/yara-rulz", + "https://github.com/JPCERTCC/jpcert-yara", + "https://github.com/SIFalcon/Detection/", + "https://github.com/bartblaze/Yara-rules", + "https://github.com/intezer/yara-rules", + "https://github.com/advanced-threat-research/Yara-Rules", + "https://github.com/stratosphereips/yara-rules", + "https://github.com/reversinglabs/reversinglabs-yara-rules", + "https://github.com/sbousseaden/YaraHunts", + "https://github.com/InQuest/yara-rules", + "https://github.com/StrangerealIntel/DailyIOC", + "https://github.com/mandiant/red_team_tool_countermeasures", + "https://github.com/fboldewin/YARA-rules", + "https://github.com/dr4k0nia/yara-rules", + "https://github.com/Yara-Rules/rules.git", + "https://github.com/Neo23x0/signature-base.git", + "https://yaraify-api.abuse.ch/download/yaraify-rules.zip", + ], + }, + "APKiD": {}, + "Floss": { + "rank_strings": { + "stack_strings": False, + "static_strings": False, + "decoded_strings": False, + }, + "max_no_of_strings": { + "stack_strings": 1000, + "static_strings": 1000, + "decoded_strings": 1000, + }, + }, + "IPApi": {}, + "ClamAV": {}, + "Robtex": {}, + "Tranco": {}, + "PE_Info": {}, + "URLhaus": {}, + "DNStwist": { + "tld": True, + "ssdeep": True, + "mxcheck": True, + "tld_dict": "abused_tlds.dict", + }, + "Doc_Info": {"additional_passwords_to_check": []}, + "ELF_Info": {}, + "PDF_Info": {}, + "Rtf_Info": {}, + "Suricata": {"reload_rules": False, "extended_logs": False}, + "Capa_Info": {}, + "CyberChef": { + "output_type": "", + "recipe_code": [], + "recipe_name": "to decimal", + }, + "File_Info": {}, + "Onionscan": {"verbose": True, "torProxyAddress": ""}, + "SpeakEasy": {}, + "ThreatFox": {}, + "CRXcavator": {}, + "CheckDMARC": {}, + "Google_DNS": {"query_type": "A"}, + "Phishstats": {}, + "TorProject": {}, + "Classic_DNS": {"query_type": "A"}, + "PEframe_Scan": {}, + "Qiling_Linux": { + "os": "linux", + "arch": "x86", + "profile": "", + "shellcode": False, + }, + "Quark_Engine": {}, + "Strings_Info": {}, + "Thug_URL_Info": { + "proxy": "", + "use_proxy": False, + "dom_events": "click,mouseover", + "user_agent": "winxpie60", + "enable_awis": True, + "enable_image_processing_analysis": True, + }, + "FireHol_IPList": {"list_names": ["firehol_level1.netset"]}, + "GreyNoiseAlpha": {"greynoise_api_version": "v1"}, + "Qiling_Windows": { + "os": "windows", + "arch": "x86", + "profile": "", + "shellcode": False, + }, + "Signature_Info": {}, + "Thug_HTML_Info": { + "proxy": "", + "use_proxy": False, + "dom_events": "click,mouseover", + "user_agent": "winxpie60", + "enable_awis": True, + "enable_image_processing_analysis": True, + }, + "FileScan_Search": {}, + "TalosReputation": {}, + "Darksearch_Query": {"pages": 10, "proxies": {}}, + "Threatminer_PDNS": {}, + "YARAify_Generics": {"query": "get_yara", "result_max": 25}, + "Capa_Info_Shellcode": {"arch": "64", "shellcode": True}, + "Mnemonic_PassiveDNS": {"limit": 1000, "cof_format": True}, + "SpeakEasy_Shellcode": {"arch": "x64", "shellcode": True, "raw_offset": 0}, + "WhoIs_RipeDB_Search": {}, + "FileScan_Upload_File": {}, + "BoxJS_Scan_JavaScript": {}, + "CryptoScamDB_CheckAPI": {}, + "MalwareBazaar_Get_File": {}, + "Qiling_Linux_Shellcode": { + "os": "linux", + "arch": "x86", + "profile": "", + "shellcode": True, + }, + "Stratosphere_Blacklist": {}, + "Xlm_Macro_Deobfuscator": { + "passwords_to_check": ["agenzia", "inps", "coronavirus"] + }, + "Qiling_Windows_Shellcode": { + "os": "windows", + "arch": "x86", + "profile": "", + "shellcode": True, + }, + "Quad9_Malicious_Detector": {}, + "HashLookupServer_Get_File": {"hashlookup_server": ""}, + "DNS0_EU_Malicious_Detector": {}, + "Cymru_Hash_Registry_Get_File": {}, + "MalwareBazaar_Get_Observable": {}, + "CloudFlare_Malicious_Detector": {}, + "HashLookupServer_Get_Observable": {"hashlookup_server": ""}, + "MalwareBazaar_Google_Observable": {}, + "Cymru_Hash_Registry_Get_Observable": {}, + }, + "connectors": {}, + "visualizers": {}, + }, + "scan_mode": 2, + "scan_check_time": "1 00:00:00", + "tlp": "CLEAR", + "owner": None, + "disabled_in_organizations": [], + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("playbooks_manager", "0002_0000_playbook_config_dns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0002_playbook_config_popular_ip_reputation_services.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0002_playbook_config_popular_ip_reputation_services.py new file mode 100644 index 0000000..e097c1c --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0002_playbook_config_popular_ip_reputation_services.py @@ -0,0 +1,112 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Popular_IP_Reputation_Services", + "analyzers": [ + "AbuseIPDB", + "Crowdsec", + "FireHol_IPList", + "GreedyBear", + "GreyNoiseCommunity", + "InQuest_REPdb", + "OTXQuery", + "TalosReputation", + "ThreatFox", + "TorProject", + "URLhaus", + "VirusTotal_v3_Get_Observable", + ], + "connectors": [], + "pivots": [], + "for_organization": False, + "description": "Collection of the most popular and free reputation analyzers for IP addresses", + "disabled": False, + "type": ["ip"], + "runtime_configuration": {"analyzers": {}, "connectors": {}, "visualizers": {}}, + "scan_mode": 2, + "scan_check_time": "1 00:00:00", + "tlp": "AMBER", + "owner": None, + "disabled_in_organizations": [], + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("playbooks_manager", "0002_0001_playbook_config_free_to_use_analyzers"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0003_playbook_config_popular_url_reputation_services.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0003_playbook_config_popular_url_reputation_services.py new file mode 100644 index 0000000..958a59f --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0003_playbook_config_popular_url_reputation_services.py @@ -0,0 +1,114 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Popular_URL_Reputation_Services", + "analyzers": [ + "CloudFlare_Malicious_Detector", + "DNS0_EU_Malicious_Detector", + "GoogleSafebrowsing", + "InQuest_REPdb", + "OTXQuery", + "PhishingArmy", + "Phishtank", + "Quad9_Malicious_Detector", + "ThreatFox", + "URLhaus", + "VirusTotal_v3_Get_Observable", + ], + "connectors": [], + "pivots": [], + "for_organization": False, + "description": "Collection of the most popular and free reputation analyzers for URLs and Domains", + "disabled": False, + "type": ["url", "domain"], + "runtime_configuration": {"analyzers": {}, "connectors": {}, "visualizers": {}}, + "scan_mode": 2, + "scan_check_time": "1 00:00:00", + "tlp": "AMBER", + "owner": None, + "disabled_in_organizations": [], + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "playbooks_manager", + "0002_0002_playbook_config_popular_ip_reputation_services", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0004_playbook_config_sample_static_analysis.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0004_playbook_config_sample_static_analysis.py new file mode 100644 index 0000000..e433523 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0002_0004_playbook_config_sample_static_analysis.py @@ -0,0 +1,126 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Sample_Static_Analysis", + "analyzers": [ + "APKiD", + "BoxJS", + "Capa_Info", + "ClamAV", + "Cymru_Hash_Registry_Get_File", + "Doc_Info", + "ELF_Info", + "File_Info", + "Floss", + "HashLookupServer_Get_File", + "HybridAnalysis_Get_File", + "MalwareBazaar_Get_File", + "OTX_Check_Hash", + "OneNote_Info", + "PDF_Info", + "PE_Info", + "Quark_Engine", + "Rtf_Info", + "Signature_Info", + "Strings_Info", + "Xlm_Macro_Deobfuscator", + "YARAify_File_Search", + "Yara", + ], + "connectors": [], + "pivots": [], + "for_organization": False, + "description": "Playbooks containing the majority of the Internal Static Analysis tools", + "disabled": False, + "type": ["file"], + "runtime_configuration": {"analyzers": {}, "connectors": {}, "visualizers": {}}, + "scan_mode": 2, + "scan_check_time": "1 00:00:00", + "tlp": "AMBER", + "owner": None, + "disabled_in_organizations": [], + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ( + "playbooks_manager", + "0002_0003_playbook_config_popular_url_reputation_services", + ), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0023_2_change_primary_key.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0023_2_change_primary_key.py new file mode 100644 index 0000000..fa17946 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0023_2_change_primary_key.py @@ -0,0 +1,57 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.contrib.postgres.fields +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + Playbook = apps.get_model("playbooks_manager", "PlaybookConfig") + Playbook.objects.update( + analyzers2=ArraySubquery( + Playbook.objects.filter(pk=models.OuterRef("pk")).values("analyzers__name") + ), + connectors2=ArraySubquery( + Playbook.objects.filter(pk=models.OuterRef("pk")).values("connectors__name") + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_1_change_primary_key"), + ("connectors_manager", "0029_1_change_primary_key"), + ("playbooks_manager", "0002_0004_playbook_config_sample_static_analysis"), + ("visualizers_manager", "0001_initial_squashed"), + ] + + operations = [ + migrations.AddField( + model_name="playbookconfig", + name="analyzers2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="connectors2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate), + migrations.RemoveField( + model_name="playbookconfig", + name="analyzers", + ), + migrations.RemoveField( + model_name="playbookconfig", + name="connectors", + ), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0023_4_change_primary_key.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0023_4_change_primary_key.py new file mode 100644 index 0000000..0302aba --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0023_4_change_primary_key.py @@ -0,0 +1,51 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +def migrate(apps, schema_editor): + Playbook = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + ConnectorConfig = apps.get_model("connectors_manager", "ConnectorConfig") + for playbook in Playbook.objects.all(): + acs = AnalyzerConfig.objects.filter(name__in=playbook.analyzers2).values_list( + "pk", flat=True + ) + ccs = ConnectorConfig.objects.filter(name__in=playbook.connectors2).values_list( + "pk", flat=True + ) + playbook.analyzers.set(acs) + playbook.connectors.set(ccs) + + +class Migration(migrations.Migration): + dependencies = [ + ("analyzers_manager", "0058_3_change_primary_key"), + ("connectors_manager", "0029_3_change_primary_key"), + ("playbooks_manager", "0023_2_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="playbookconfig", + name="analyzers", + field=models.ManyToManyField( + blank=True, + related_name="playbooks", + to="analyzers_manager.AnalyzerConfig", + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="connectors", + field=models.ManyToManyField( + blank=True, + related_name="playbooks", + to="connectors_manager.ConnectorConfig", + ), + ), + migrations.RunPython( + migrate, + ), + migrations.RemoveField(model_name="playbookconfig", name="analyzers2"), + migrations.RemoveField(model_name="playbookconfig", name="connectors2"), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_1_change_primary_key.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_1_change_primary_key.py new file mode 100644 index 0000000..be67e80 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_1_change_primary_key.py @@ -0,0 +1,111 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.contrib.postgres.fields +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + PlaybookConfig.objects.update( + pivots2=ArraySubquery( + PlaybookConfig.objects.filter(pk=models.OuterRef("pk")).values( + "pivots__name" + ) + ), + analyzers2=ArraySubquery( + PlaybookConfig.objects.filter(pk=models.OuterRef("pk")).values( + "analyzers__name" + ) + ), + connectors2=ArraySubquery( + PlaybookConfig.objects.filter(pk=models.OuterRef("pk")).values( + "connectors__name" + ) + ), + tags2=ArraySubquery( + PlaybookConfig.objects.filter(pk=models.OuterRef("pk")).values("tags__pk") + ), + disabled2=ArraySubquery( + PlaybookConfig.objects.filter(pk=models.OuterRef("pk")).values( + "disabled_in_organizations__pk" + ) + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0023_4_change_primary_key"), + ("pivots_manager", "0001_2_initial_squashed"), + ("api_app", "0057_4_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="playbookconfig", + name="disabled2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.IntegerField(), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="pivots2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="connectors2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="analyzers2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="tags2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.IntegerField(), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate), + migrations.RemoveField(model_name="playbookconfig", name="pivots"), + migrations.RemoveField( + model_name="playbookconfig", + name="analyzers", + ), + migrations.RemoveField( + model_name="playbookconfig", + name="connectors", + ), + migrations.RemoveField( + model_name="playbookconfig", + name="tags", + ), + migrations.RemoveField( + model_name="playbookconfig", name="disabled_in_organizations" + ), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_3_change_primary_key.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_3_change_primary_key.py new file mode 100644 index 0000000..e2efb7a --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_3_change_primary_key.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_1_change_primary_key"), + ("api_app", "0057_2_change_primary_key"), + ] + + operations = [ + migrations.RunSQL( + 'ALTER TABLE "playbooks_manager_playbookconfig" DROP CONSTRAINT "playbooks_manager_playbookconfig_pkey" CASCADE;' + ), + migrations.AlterField( + model_name="playbookconfig", + name="name", + field=models.CharField( + max_length=100, + unique=True, + primary_key=False, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", "Your name should match the [A-Za-z0-9_] characters" + ) + ], + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="id", + field=models.BigAutoField( + auto_created=True, serialize=False, verbose_name="ID", primary_key=True + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_4_change_primary_key.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_4_change_primary_key.py new file mode 100644 index 0000000..065e9d8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0024_4_change_primary_key.py @@ -0,0 +1,95 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + PivotConfig = apps.get_model("pivots_manager", "PivotConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + ConnectorConfig = apps.get_model("connectors_manager", "ConnectorConfig") + Organization = apps.get_model("certego_saas_organization", "Organization") + Tag = apps.get_model("api_app", "Tag") + for playbook in PlaybookConfig.objects.all(): + pivots = PivotConfig.objects.filter(name__in=playbook.pivots2).values_list( + "pk", flat=True + ) + acs = AnalyzerConfig.objects.filter(name__in=playbook.analyzers2).values_list( + "pk", flat=True + ) + ccs = ConnectorConfig.objects.filter(name__in=playbook.connectors2).values_list( + "pk", flat=True + ) + tags = Tag.objects.filter(pk__in=playbook.tags2).values_list("pk", flat=True) + playbook.pivots.set(pivots) + playbook.analyzers.set(acs) + playbook.connectors.set(ccs) + playbook.tags.set(tags) + if playbook.disabled2: + OrganizationPluginConfiguration = apps.get_model( + "api_app", "OrganizationPluginConfiguration" + ) + ContentType = apps.get_model("contenttypes", "ContentType") + ct = ContentType.objects.get_for_model(playbook) + for org in playbook.disabled2: + if org: + OrganizationPluginConfiguration.objects.create( + organization=Organization.objects.get(pk=org), + object_id=playbook.pk, + content_type=ct, + disabled=True, + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("playbooks_manager", "0024_3_change_primary_key"), + ("pivots_manager", "0001_2_initial_squashed"), + ("analyzers_manager", "0058_3_change_primary_key"), + ("connectors_manager", "0029_3_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="playbookconfig", + name="pivots", + field=models.ManyToManyField( + blank=True, + related_name="used_by_playbooks", + to="pivots_manager.PivotConfig", + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="analyzers", + field=models.ManyToManyField( + blank=True, + related_name="playbooks", + to="analyzers_manager.AnalyzerConfig", + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="connectors", + field=models.ManyToManyField( + blank=True, + related_name="playbooks", + to="connectors_manager.ConnectorConfig", + ), + ), + migrations.AddField( + model_name="playbookconfig", + name="tags", + field=models.ManyToManyField( + blank=True, + related_name="playbooks", + to="api_app.Tag", + ), + ), + migrations.RunPython(migrate), + migrations.RemoveField(model_name="playbookconfig", name="pivots2"), + migrations.RemoveField(model_name="playbookconfig", name="analyzers2"), + migrations.RemoveField(model_name="playbookconfig", name="connectors2"), + migrations.RemoveField(model_name="playbookconfig", name="tags2"), + migrations.RemoveField(model_name="playbookconfig", name="disabled2"), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0025_add_mmdb_analyzer_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0025_add_mmdb_analyzer_free_to_use.py new file mode 100644 index 0000000..dbe1aa7 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0025_add_mmdb_analyzer_free_to_use.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Mmdb_server").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Mmdb_server").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_4_change_primary_key"), + ("analyzers_manager", "0062_analyzer_config_mmdb_server"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0026_add_zippy_scan_to_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0026_add_zippy_scan_to_free_to_use.py new file mode 100644 index 0000000..e812bd1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0026_add_zippy_scan_to_free_to_use.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Zippy_scan").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Zippy_scan").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0025_add_mmdb_analyzer_free_to_use"), + ("analyzers_manager", "0064_analyzer_config_zippy_scan"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0027_add_feodo_tracker_to_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0027_add_feodo_tracker_to_free_to_use.py new file mode 100644 index 0000000..ab0f522 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0027_add_feodo_tracker_to_free_to_use.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Feodo_Tracker").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Feodo_Tracker").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0026_add_zippy_scan_to_free_to_use"), + ("analyzers_manager", "0068_analyzer_config_feodo_tracker"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0028_add_bgp_ranking_to_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0028_add_bgp_ranking_to_free_to_use.py new file mode 100644 index 0000000..ed022d4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0028_add_bgp_ranking_to_free_to_use.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="BGP_Ranking").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="BGP_Ranking").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0027_add_feodo_tracker_to_free_to_use"), + ("analyzers_manager", "0069_analyzer_config_bgp_ranking"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0029_add_tor_nodes_danmeuk_analyzer_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0029_add_tor_nodes_danmeuk_analyzer_free_to_use.py new file mode 100644 index 0000000..e7010c1 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0029_add_tor_nodes_danmeuk_analyzer_free_to_use.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Tor_Nodes_DanMeUk").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Tor_Nodes_DanMeUk").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0028_add_bgp_ranking_to_free_to_use"), + ("analyzers_manager", "0071_analyzer_config_tor_nodes_danmeuk"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0030_add_tweetfeeds_to_free_analyzers.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0030_add_tweetfeeds_to_free_analyzers.py new file mode 100644 index 0000000..c7b0dc5 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0030_add_tweetfeeds_to_free_analyzers.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="TweetFeed").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="TweetFeed").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0029_add_tor_nodes_danmeuk_analyzer_free_to_use"), + ("analyzers_manager", "0072_analyzer_config_tweetfeed"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0031_add_hfinger_analyzer_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0031_add_hfinger_analyzer_free_to_use.py new file mode 100644 index 0000000..dfba5cb --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0031_add_hfinger_analyzer_free_to_use.py @@ -0,0 +1,34 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Hfinger").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Hfinger").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0030_add_tweetfeeds_to_free_analyzers"), + ("analyzers_manager", "0078_analyzer_config_hfinger"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0032_delete_dns0_playbook_free_to_use_analyzers.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0032_delete_dns0_playbook_free_to_use_analyzers.py new file mode 100644 index 0000000..7745a42 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0032_delete_dns0_playbook_free_to_use_analyzers.py @@ -0,0 +1,45 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.filter(name="FREE_TO_USE_ANALYZERS").first() + if pc: + for analyzer_config_name in ["DNS0_rrsets_name", "DNS0_names"]: + analyzer_config = AnalyzerConfig.objects.filter( + name=analyzer_config_name + ).first() + if analyzer_config: + pc.analyzers.remove(analyzer_config.id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.filter(name="FREE_TO_USE_ANALYZERS").first() + if pc: + for analyzer_config_name in ["DNS0_rrsets_name", "DNS0_names"]: + analyzer_config = AnalyzerConfig.objects.filter( + name=analyzer_config_name + ).first() + if analyzer_config: + pc.analyzers.add(analyzer_config.id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0031_add_hfinger_analyzer_free_to_use"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0033_playbook_config_send_abuse_email.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0033_playbook_config_send_abuse_email.py new file mode 100644 index 0000000..2decf60 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0033_playbook_config_send_abuse_email.py @@ -0,0 +1,119 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Send_Abuse_Email", + "analyzers": [], + "connectors": ["AbuseSubmitter"], + "pivots": [], + "for_organization": False, + "description": "Playbook containing the AbuseSubmitter connector to send an email " + "to request to take down a malicious domain. " + "It is executed after the Abuse_IP playbook.", + "disabled": False, + "type": ["generic"], + "runtime_configuration": { + "pivots": {}, + "analyzers": {}, + "connectors": {}, + "visualizers": {}, + }, + "scan_mode": 1, + "scan_check_time": None, + "tlp": "AMBER", + "owner": None, + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("playbooks_manager", "0032_delete_dns0_playbook_free_to_use_analyzers"), + ("connectors_manager", "0031_connector_config_abusesubmitter"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0034_playbook_config_abuse_ip.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0034_playbook_config_abuse_ip.py new file mode 100644 index 0000000..f3b9685 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0034_playbook_config_abuse_ip.py @@ -0,0 +1,119 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Abuse_IP", + "analyzers": ["Abusix"], + "connectors": [], + "pivots": ["AbuseIpToSubmission"], + "for_organization": False, + "description": "Playbook containing the Abusix analyzer. " + "It is executed after the Takedown_Request playbook.", + "disabled": False, + "type": ["ip"], + "runtime_configuration": { + "pivots": {}, + "analyzers": {}, + "connectors": {}, + "visualizers": {}, + }, + "scan_mode": 1, + "scan_check_time": None, + "tlp": "AMBER", + "owner": None, + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("playbooks_manager", "0033_playbook_config_send_abuse_email"), + ("analyzers_manager", "0081_adjust_abusix"), + ("pivots_manager", "0026_pivot_config_abuseiptosubmission"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0035_playbook_config_takedown_request.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0035_playbook_config_takedown_request.py new file mode 100644 index 0000000..7e5cd10 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0035_playbook_config_takedown_request.py @@ -0,0 +1,124 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Takedown_Request", + "analyzers": [ + "Classic_DNS", + "CloudFlare_DNS", + "DNS0_EU", + "Google_DNS", + "Quad9_DNS", + ], + "connectors": [], + "pivots": ["TakedownRequestToAbuseIp"], + "for_organization": False, + "description": "Start investigation to request to take down a malicious domain. " + "A mail will be sent to the domain's abuse contacts found.", + "disabled": False, + "type": ["domain"], + "runtime_configuration": { + "pivots": {}, + "analyzers": {}, + "connectors": {}, + "visualizers": {}, + }, + "scan_mode": 1, + "scan_check_time": None, + "tlp": "AMBER", + "owner": None, + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("playbooks_manager", "0034_playbook_config_abuse_ip"), + ("pivots_manager", "0027_pivot_config_takedownrequesttoabuseip"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0036_static_analyzer_remove_visualizer.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0036_static_analyzer_remove_visualizer.py new file mode 100644 index 0000000..e923825 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0036_static_analyzer_remove_visualizer.py @@ -0,0 +1,40 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + visualizer_config = apps.get_model("visualizers_manager", "VisualizerConfig") + vc = visualizer_config.objects.filter(name="Yara").first() + if vc: + pc = playbook_config.objects.filter(name="Sample_Static_Analysis").first() + if pc: + vc.playbooks.remove(pc.id) + vc.full_clean() + vc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + visualizer_config = apps.get_model("visualizers_manager", "VisualizerConfig") + vc = visualizer_config.objects.filter(name="Yara").first() + if vc: + pc = playbook_config.objects.filter(name="Sample_Static_Analysis").first() + if pc: + vc.playbooks.add(pc.id) + vc.full_clean() + vc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0035_playbook_config_takedown_request"), + ("visualizers_manager", "0037_4_change_primary_key"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0037_playbookconfig_starting.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0037_playbookconfig_starting.py new file mode 100644 index 0000000..843cd3d --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0037_playbookconfig_starting.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.11 on 2024-05-07 13:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0036_static_analyzer_remove_visualizer"), + ] + + operations = [ + migrations.AddField( + model_name="playbookconfig", + name="starting", + field=models.BooleanField( + default=True, + help_text="If False, the playbook can only be executed by pivots", + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0038_playbooks_not_starting.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0038_playbooks_not_starting.py new file mode 100644 index 0000000..4099087 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0038_playbooks_not_starting.py @@ -0,0 +1,29 @@ +# Generated by Django 4.2.11 on 2024-05-07 13:54 + +from django.db import migrations + +playbooks_to_change = ["Abuse_IP"] + + +def migrate(apps, schema_editor): + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + for pc in PlaybookConfig.objects.filter(name__in=playbooks_to_change): + pc.starting = False + pc.save() + + +def reverse_migrate(apps, schema_editor): + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + for pc in PlaybookConfig.objects.filter(name__in=playbooks_to_change): + pc.starting = True + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0037_playbookconfig_starting"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0039_alter_playbookconfig_scan_check_time_and_more.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0039_alter_playbookconfig_scan_check_time_and_more.py new file mode 100644 index 0000000..fdd67c6 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0039_alter_playbookconfig_scan_check_time_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.11 on 2024-05-09 12:55 + +import datetime + +from django.db import migrations, models + + +def migrate(apps, schema_editor): + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + PlaybookConfig.objects.filter(starting=False).update( + scan_mode=1, scan_check_time=None + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0038_playbooks_not_starting"), + ] + + operations = [ + migrations.RunPython(migrate, migrations.RunPython.noop), + migrations.AlterField( + model_name="playbookconfig", + name="scan_check_time", + field=models.DurationField( + blank=True, + default=datetime.timedelta(days=1), + help_text="Time range checked if the scan_mode is set to `check_previous_analysis`", + null=True, + ), + ), + migrations.AlterField( + model_name="playbookconfig", + name="scan_mode", + field=models.IntegerField( + choices=[(1, "Force New Analysis"), (2, "Check Previous Analysis")], + default=2, + help_text="If it's not a starting playbook, this must be set to `check_previous_analysis`", + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0040_alter_domain_reputation_playbook.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0040_alter_domain_reputation_playbook.py new file mode 100644 index 0000000..53b73bf --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0040_alter_domain_reputation_playbook.py @@ -0,0 +1,35 @@ +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + analyzer_config = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.filter(name="Popular_URL_Reputation_Services").first() + if pc: + ac = analyzer_config.objects.filter(name="Tranco").first() + if ac: + pc.analyzers.add(ac) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + analyzer_config = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc = playbook_config.objects.filter(name="Popular_URL_Reputation_Services").first() + if pc: + ac = analyzer_config.objects.filter(name="Tranco").first() + if ac: + pc.analyzers.remove(ac) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0039_alter_playbookconfig_scan_check_time_and_more"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0041_add_permhash_to_free_analyzers_and_static_analyzers.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0041_add_permhash_to_free_analyzers_and_static_analyzers.py new file mode 100644 index 0000000..c6913d3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0041_add_permhash_to_free_analyzers_and_static_analyzers.py @@ -0,0 +1,42 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc1 = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc1.analyzers.add(AnalyzerConfig.objects.get(name="Permhash").id) + pc1.full_clean() + pc1.save() + pc2 = playbook_config.objects.get(name="Sample_Static_Analysis") + pc2.analyzers.add(AnalyzerConfig.objects.get(name="Permhash").id) + pc2.full_clean() + pc2.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + pc1 = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc1.analyzers.remove(AnalyzerConfig.objects.get(name="Permhash").id) + pc1.full_clean() + pc1.save() + pc2 = playbook_config.objects.get(name="Sample_Static_Analysis") + pc2.analyzers.remove(AnalyzerConfig.objects.get(name="Permhash").id) + pc2.full_clean() + pc2.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0040_alter_domain_reputation_playbook"), + ("analyzers_manager", "0085_analyzer_config_permhash"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0042_add_blint_to_free_analyzers_and_static_analyzers.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0042_add_blint_to_free_analyzers_and_static_analyzers.py new file mode 100644 index 0000000..450a923 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0042_add_blint_to_free_analyzers_and_static_analyzers.py @@ -0,0 +1,49 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Blint").id) + pc.full_clean() + pc.save() + + pc = playbook_config.objects.get(name="Sample_Static_Analysis") + pc.analyzers.add(AnalyzerConfig.objects.get(name="Blint").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Blint").id) + pc.full_clean() + pc.save() + + pc = playbook_config.objects.get(name="Sample_Static_Analysis") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="Blint").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ( + "playbooks_manager", + "0041_add_permhash_to_free_analyzers_and_static_analyzers", + ), + ("analyzers_manager", "0086_analyzer_config_blint"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0043_playbook_config_pcap_analysis.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0043_playbook_config_pcap_analysis.py new file mode 100644 index 0000000..de9293c --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0043_playbook_config_pcap_analysis.py @@ -0,0 +1,117 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "analyzers": ["Hfinger", "Suricata"], + "connectors": [], + "pivots": [], + "for_organization": False, + "name": "PCAP_Analysis", + "description": "A Playbook containing the analyzers that support PCAP analysis", + "disabled": False, + "type": ["file"], + "runtime_configuration": { + "pivots": {}, + "analyzers": {}, + "connectors": {}, + "visualizers": {}, + }, + "scan_mode": 2, + "scan_check_time": "1 00:00:00", + "tlp": "RED", + "starting": True, + "owner": None, + "tags": [], + "model": "playbooks_manager.PlaybookConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + def _get_obj(Model, other_model, value): + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + if isinstance(value, int): + if Model.__name__ == "PluginConfig": + value = other_model.objects.get(name=plugin["name"]) + else: + value = other_model.objects.get(pk=value) + else: + value = other_model.objects.get(name=value) + return value + + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + value = _get_obj(Model, other_model, value) + elif type(getattr(Model, field)) in [ManyToManyDescriptor] and value: + other_model = getattr(Model, field).rel.model + value = [_get_obj(Model, other_model, val) for val in value] + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + value = _get_real_obj(Model, field, value) + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + if value is not None: + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + atomic = False + dependencies = [ + ("api_app", "0062_alter_parameter_python_module"), + ("playbooks_manager", "0042_add_blint_to_free_analyzers_and_static_analyzers"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0044_add_cycat_to_free_to_use.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0044_add_cycat_to_free_to_use.py new file mode 100644 index 0000000..38cbb6e --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/0044_add_cycat_to_free_to_use.py @@ -0,0 +1,49 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +from django.db import migrations + + +def migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.add(AnalyzerConfig.objects.get(name="CyCat").id) + pc.full_clean() + pc.save() + + pc = playbook_config.objects.get(name="Sample_Static_Analysis") + pc.analyzers.add(AnalyzerConfig.objects.get(name="CyCat").id) + pc.full_clean() + pc.save() + + +def reverse_migrate(apps, schema_editor): + playbook_config = apps.get_model("playbooks_manager", "PlaybookConfig") + AnalyzerConfig = apps.get_model("analyzers_manager", "AnalyzerConfig") + + pc = playbook_config.objects.get(name="FREE_TO_USE_ANALYZERS") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="CyCat").id) + pc.full_clean() + pc.save() + + pc = playbook_config.objects.get(name="Sample_Static_Analysis") + pc.analyzers.remove(AnalyzerConfig.objects.get(name="CyCat").id) + pc.full_clean() + pc.save() + + +class Migration(migrations.Migration): + dependencies = [ + ( + "playbooks_manager", + "0043_playbook_config_pcap_analysis", + ), + ("analyzers_manager", "0090_analyzer_config_cycat"), + ] + + operations = [ + migrations.RunPython(migrate, reverse_migrate), + ] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/playbooks_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/models.py b/Submodules/IntelOwl/api_app/playbooks_manager/models.py new file mode 100644 index 0000000..f31b565 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/models.py @@ -0,0 +1,116 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import datetime + +from django.core.exceptions import ValidationError +from django.db import models + +from api_app.analyzers_manager.constants import AllTypes +from api_app.choices import TLP, ScanMode +from api_app.defaults import default_runtime +from api_app.fields import ChoiceArrayField +from api_app.interfaces import OwnershipAbstractModel +from api_app.models import AbstractConfig, Tag +from api_app.playbooks_manager.queryset import PlaybookConfigQuerySet +from api_app.validators import validate_runtime_configuration + + +class PlaybookConfig(AbstractConfig, OwnershipAbstractModel): + objects = PlaybookConfigQuerySet.as_manager() + type = ChoiceArrayField( + models.CharField(choices=AllTypes.choices, null=False, max_length=50) + ) + + analyzers = models.ManyToManyField( + "analyzers_manager.AnalyzerConfig", related_name="playbooks", blank=True + ) + connectors = models.ManyToManyField( + "connectors_manager.ConnectorConfig", related_name="playbooks", blank=True + ) + pivots = models.ManyToManyField( + "pivots_manager.PivotConfig", related_name="used_by_playbooks", blank=True + ) + + runtime_configuration = models.JSONField( + blank=True, + default=default_runtime, + null=False, + validators=[validate_runtime_configuration], + ) + scan_mode = models.IntegerField( + choices=ScanMode.choices, + null=False, + blank=False, + default=ScanMode.CHECK_PREVIOUS_ANALYSIS.value, + help_text=( + "If it's not a starting playbook," + " this must be set to `check_previous_analysis`" + ), + ) + scan_check_time = models.DurationField( + null=True, + blank=True, + default=datetime.timedelta(hours=24), + help_text=( + "Time range checked if the scan_mode is set to `check_previous_analysis`" + ), + ) + + tags = models.ManyToManyField(Tag, related_name="playbooks", blank=True) + + tlp = models.CharField(max_length=8, choices=TLP.choices) + + starting = models.BooleanField( + default=True, help_text="If False, the playbook can only be executed by pivots" + ) + + class Meta: + ordering = ["name", "disabled"] + indexes = OwnershipAbstractModel.Meta.indexes + unique_together = [["name", "owner"]] + + def _generate_tlp(self) -> str: + tlps = [ + TLP[x] + for x in list(self.analyzers.values_list("maximum_tlp", flat=True)) + + list(self.connectors.values_list("maximum_tlp", flat=True)) + ] + # analyzer -> amber + # playbook -> green => analyzer it is executed + # -------------- + # analyzer -> amber + # playbook -> red => analyzer it is not executed + # ========> the playbook tlp is the minimum of all tlp of all plugins + return min(tlps, default=TLP.CLEAR).value + + def clean_scan(self): + if ( + self.scan_mode == ScanMode.FORCE_NEW_ANALYSIS.value + and self.scan_check_time is not None + ): + raise ValidationError( + f"You can't have set mode to {ScanMode.FORCE_NEW_ANALYSIS.name}" + " and have check_time set" + ) + elif ( + self.scan_mode == ScanMode.CHECK_PREVIOUS_ANALYSIS.value + and self.scan_check_time is None + ): + raise ValidationError( + f"You can't have set mode to {ScanMode.CHECK_PREVIOUS_ANALYSIS.name}" + " and not have check_time set" + ) + + def clean_starting(self): + if not self.starting and self.scan_mode != ScanMode.FORCE_NEW_ANALYSIS.value: + raise ValidationError( + "Not starting playbooks must always force new analysis" + ) + + def clean(self) -> None: + super().clean() + self.clean_scan() + self.clean_starting() + + def is_sample(self) -> bool: + return AllTypes.FILE.value in self.type diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/queryset.py b/Submodules/IntelOwl/api_app/playbooks_manager/queryset.py new file mode 100644 index 0000000..35305c9 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/queryset.py @@ -0,0 +1,82 @@ +import datetime +from typing import Union + +from django.db.models import F, Func, OuterRef, QuerySet, Subquery, Value +from django.utils.timezone import now + +from api_app.models import Job +from api_app.queryset import AbstractConfigQuerySet, ModelWithOwnershipQuerySet +from certego_saas.apps.user.models import User + + +class PlaybookConfigQuerySet(AbstractConfigQuerySet, ModelWithOwnershipQuerySet): + @staticmethod + def _subquery_weight_user(user: User) -> Subquery: + return Subquery( + Job.objects.prefetch_related("user") + .filter( + user__pk=user.pk, + playbook_to_execute=OuterRef("pk"), + finished_analysis_time__gte=now() - datetime.timedelta(days=30), + ) + .annotate(count=Func(F("pk"), function="Count")) + .values("count") + ) + + @staticmethod + def _subquery_weight_org(user: User) -> Union[Subquery, Value]: + if user.has_membership(): + return Subquery( + Job.objects.prefetch_related("user") + .filter( + user__membership__organization__pk=user.membership.organization.pk, + playbook_to_execute=OuterRef("pk"), + finished_analysis_time__gte=now() - datetime.timedelta(days=30), + ) + .exclude(user__pk=user.pk) + .annotate(count=Func(F("pk"), function="Count")) + .values("count") + ) + return Value(0) + + @staticmethod + def _subquery_weight_other(user: User) -> Subquery: + if user.has_membership(): + return Subquery( + Job.objects.filter( + playbook_to_execute=OuterRef("pk"), + finished_analysis_time__gte=now() - datetime.timedelta(days=30), + ) + .exclude( + user__membership__organization__pk=user.membership.organization.pk + ) + .annotate(count=Func(F("pk"), function="Count")) + .values("count") + ) + return Subquery( + Job.objects.prefetch_related("user") + .filter(playbook_to_execute=OuterRef("pk")) + .exclude(user__pk=user.pk) + .annotate(count=Func(F("pk"), function="Count")) + .values("count") + ) + + def ordered_for_user(self, user: User) -> QuerySet: + USER_WEIGHT_MULTIPLICATIVE = 3 + ORG_WEIGHT_MULTIPLICATIVE = 2 + OTHER_WEIGHT_MULTIPLICATIVE = 1 + + return ( + self.prefetch_related("executed_in_jobs") + .annotate( + user_weight=self._subquery_weight_user(user), + org_weight=self._subquery_weight_org(user), + other_weight=self._subquery_weight_other(user), + ) + .annotate( + weight=(F("user_weight") * USER_WEIGHT_MULTIPLICATIVE) + + (F("org_weight") * ORG_WEIGHT_MULTIPLICATIVE) + + (F("other_weight") * OTHER_WEIGHT_MULTIPLICATIVE) + ) + .order_by("-weight", "name") + ) diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/serializers.py b/Submodules/IntelOwl/api_app/playbooks_manager/serializers.py new file mode 100644 index 0000000..200b9cb --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/serializers.py @@ -0,0 +1,107 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from rest_framework import serializers as rfs + +from api_app.analyzers_manager.constants import TypeChoices +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.choices import ScanMode +from api_app.connectors_manager.models import ConnectorConfig +from api_app.helpers import gen_random_colorhex +from api_app.models import Tag +from api_app.pivots_manager.models import PivotConfig +from api_app.playbooks_manager.fields import DayDurationField +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.serializers import ModelWithOwnershipSerializer +from api_app.serializers.job import TagSerializer +from api_app.serializers.plugin import AbstractConfigSerializerForMigration + + +class PlaybookConfigSerializerForMigration(AbstractConfigSerializerForMigration): + analyzers = rfs.SlugRelatedField(slug_field="name", many=True, read_only=True) + connectors = rfs.SlugRelatedField(slug_field="name", many=True, read_only=True) + pivots = rfs.SlugRelatedField(slug_field="name", many=True, read_only=True) + + class Meta: + model = PlaybookConfig + exclude = AbstractConfigSerializerForMigration.Meta.exclude + + +class PlaybookConfigSerializer(ModelWithOwnershipSerializer, rfs.ModelSerializer): + class Meta: + model = PlaybookConfig + fields = rfs.ALL_FIELDS + + type = rfs.ListField(child=rfs.CharField(read_only=True), read_only=True) + analyzers = rfs.SlugRelatedField( + many=True, + queryset=AnalyzerConfig.objects.all(), + required=True, + allow_empty=False, + slug_field="name", + ) + connectors = rfs.SlugRelatedField( + many=True, + queryset=ConnectorConfig.objects.all(), + required=True, + slug_field="name", + ) + pivots = rfs.SlugRelatedField( + many=True, queryset=PivotConfig.objects.all(), required=True, slug_field="name" + ) + visualizers = rfs.SlugRelatedField(read_only=True, many=True, slug_field="name") + + runtime_configuration = rfs.DictField(required=True) + + scan_mode = rfs.ChoiceField(choices=ScanMode.choices, required=True) + scan_check_time = DayDurationField(required=True, allow_null=True) + tags = TagSerializer(required=False, allow_empty=True, many=True, read_only=True) + tlp = rfs.CharField(read_only=True) + weight = rfs.IntegerField(read_only=True, required=False, allow_null=True) + is_deletable = rfs.SerializerMethodField() + tags_labels = rfs.ListField( + child=rfs.CharField(required=True), + default=list, + required=False, + write_only=True, + ) + + def get_is_deletable(self, instance: PlaybookConfig): + # if the playbook is not a default one + if instance.owner: + # it is deletable by the owner of the playbook + # or by an admin of the same organization + if instance.owner == self.context["request"].user or ( + self.context["request"].user.membership.is_admin + and self.context["request"].user.membership.organization + == instance.organization + ): + return True + return False + + @staticmethod + def validate_tags_labels(tags_labels): + for label in tags_labels: + yield Tag.objects.get_or_create( + label=label, defaults={"color": gen_random_colorhex()} + )[0] + + def validate(self, attrs): + attrs = super().validate(attrs) + if attrs.get("tags_labels"): + attrs["tags"] = attrs.pop("tags_labels", []) + return attrs + + def create(self, validated_data): + types_supported = list( + set( + [ + type_supported + for analyzer_config in validated_data["analyzers"] + for type_supported in analyzer_config.observable_supported + ] + ) + ) + if any((x.type == TypeChoices.FILE.value for x in validated_data["analyzers"])): + types_supported.append(TypeChoices.FILE) + validated_data["type"] = types_supported + return super().create(validated_data) diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/signals.py b/Submodules/IntelOwl/api_app/playbooks_manager/signals.py new file mode 100644 index 0000000..8cf87a0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/signals.py @@ -0,0 +1,81 @@ +import logging +import uuid +from typing import Type + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db.models.signals import m2m_changed +from django.dispatch import receiver + +from api_app.pivots_manager.models import PivotConfig +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.signals import migrate_finished +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +@receiver(migrate_finished) +def post_migrate_playbooks_manager( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from intel_owl.tasks import refresh_cache + + refresh_cache.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=3, + args=[PlaybookConfig.python_path], + ) + + +@receiver(m2m_changed, sender=PlaybookConfig.analyzers.through) +def m2m_changed_analyzers_playbook_config( + sender, instance: PlaybookConfig, action, reverse, model, pk_set, *args, **kwargs +): + if action == "post_add": + instance.tlp = instance._generate_tlp() + instance.save() + return instance + + +@receiver(m2m_changed, sender=PlaybookConfig.connectors.through) +def m2m_changed_connectors_playbook_config( + sender, instance: PlaybookConfig, action, reverse, model, pk_set, *args, **kwargs +): + if action == "post_add": + instance.tlp = instance._generate_tlp() + instance.save() + return instance + + +@receiver(m2m_changed, sender=PlaybookConfig.pivots.through) +def m2m_changed_pivots_playbook_config( + sender, + instance: PlaybookConfig, + action: str, + reverse, + model: Type[PivotConfig], + pk_set, + using, + *args, + **kwargs, +): + if action == "pre_add": + objects = model.objects.filter(pk__in=pk_set) + valid_pks = objects.valid( + instance.analyzers.all(), instance.connectors.all() + ).values_list("pk", flat=True) + wrong_pivots = objects.exclude(pk__in=valid_pks) + if wrong_pivots.exists(): + raise ValidationError( + f"You can't set pivot{'s' if wrong_pivots.size()> 0 else ''}" + f" {','.join(wrong_pivots.values_list('name', flat=True))} because" + " the playbook does not have all the required plugins" + ) diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/urls.py b/Submodules/IntelOwl/api_app/playbooks_manager/urls.py new file mode 100644 index 0000000..4bcdf77 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/urls.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +from .views import PlaybookConfigViewSet + +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"playbook", PlaybookConfigViewSet, basename="playbook") + +urlpatterns = [ + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/playbooks_manager/views.py b/Submodules/IntelOwl/api_app/playbooks_manager/views.py new file mode 100644 index 0000000..686fb26 --- /dev/null +++ b/Submodules/IntelOwl/api_app/playbooks_manager/views.py @@ -0,0 +1,79 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from drf_spectacular.utils import extend_schema as add_docs +from rest_framework import mixins, status +from rest_framework.decorators import action +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response + +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.playbooks_manager.serializers import PlaybookConfigSerializer +from api_app.serializers.job import ( + FileJobSerializer, + JobResponseSerializer, + ObservableAnalysisSerializer, +) +from api_app.views import AbstractConfigViewSet, ModelWithOwnershipViewSet + +logger = logging.getLogger(__name__) + + +class PlaybookConfigViewSet( + ModelWithOwnershipViewSet, AbstractConfigViewSet, mixins.CreateModelMixin +): + serializer_class = PlaybookConfigSerializer + ordering = ["-weight", "-executed_by_pivot", "name"] + permission_classes = [IsAuthenticated] + queryset = PlaybookConfig.objects.all() + + def get_queryset(self): + return ( + super() + .get_queryset() + .ordered_for_user(self.request.user) + .prefetch_related( + "analyzers", + "connectors", + "pivots", + "visualizers", + "tags", + ) + ) + + @add_docs( + description="This endpoint allows to start a Job related to an observable", + request=ObservableAnalysisSerializer, + responses={200: JobResponseSerializer}, + ) + @action(methods=["POST"], url_name="analyze_multiple_observables", detail=False) + def analyze_multiple_observables(self, request): + oas = ObservableAnalysisSerializer( + data=request.data, many=True, context={"request": request} + ) + oas.is_valid(raise_exception=True) + parent_job = oas.validated_data[0].get("parent_job", None) + jobs = oas.save(send_task=True, parent=parent_job) + return Response( + JobResponseSerializer(jobs, many=True).data, + status=status.HTTP_200_OK, + ) + + @add_docs( + description="This endpoint allows to start a Job related to a file", + request=FileJobSerializer, + responses={200: JobResponseSerializer}, + ) + @action(methods=["POST"], url_name="analyze_multiple_files", detail=False) + def analyze_multiple_files(self, request): + oas = FileJobSerializer( + data=request.data, many=True, context={"request": request} + ) + oas.is_valid(raise_exception=True) + jobs = oas.save(send_task=True) + return Response( + JobResponseSerializer(jobs, many=True).data, + status=status.HTTP_200_OK, + ) diff --git a/Submodules/IntelOwl/api_app/queryset.py b/Submodules/IntelOwl/api_app/queryset.py new file mode 100644 index 0000000..d51d571 --- /dev/null +++ b/Submodules/IntelOwl/api_app/queryset.py @@ -0,0 +1,595 @@ +import datetime +import json +import uuid +from typing import TYPE_CHECKING, Generator, Type + +from django.conf import settings +from django.contrib.postgres.expressions import ArraySubquery +from django.core.paginator import Paginator +from treebeard.mp_tree import MP_NodeQuerySet + +if TYPE_CHECKING: + from api_app.models import PythonConfig + from api_app.serializers import AbstractBIInterface + +import logging + +from celery.canvas import Signature +from django.db import models +from django.db.models import ( + BooleanField, + Case, + Exists, + F, + Func, + IntegerField, + JSONField, + OuterRef, + Q, + QuerySet, + Subquery, + Value, + When, +) +from django.db.models.functions import Cast, Coalesce +from django.db.models.lookups import Exact +from django.utils.timezone import now + +from api_app.choices import TLP, ParamTypes +from certego_saas.apps.organization.membership import Membership +from certego_saas.apps.user.models import User + +logger = logging.getLogger(__name__) + + +class SendToBiQuerySet(models.QuerySet): + @classmethod + def _get_bi_serializer_class(cls) -> Type["AbstractBIInterface"]: + raise NotImplementedError() + + @staticmethod + def _create_index_template(): + with open( + settings.CONFIG_ROOT / "elastic_search_mappings" / "intel_owl_bi.json" + ) as f: + body = json.load(f) + body["index_patterns"] = [f"{settings.ELASTICSEARCH_BI_INDEX}-*"] + settings.ELASTICSEARCH_CLIENT.indices.put_template( + name=settings.ELASTICSEARCH_BI_INDEX, body=body + ) + logger.info( + f"created template for Elastic named {settings.ELASTICSEARCH_BI_INDEX}" + ) + + def send_to_elastic_as_bi(self, max_timeout: int = 60) -> bool: + from elasticsearch.helpers import bulk + + logger.info("BI start") + self._create_index_template() + BULK_MAX_SIZE = 1000 + found_errors = False + + p = Paginator(self, BULK_MAX_SIZE) + for i in p.page_range: + page = p.get_page(i) + objects = page.object_list + serializer = self._get_bi_serializer_class()(instance=objects, many=True) + objects_serialized = serializer.data + _, errors = bulk( + settings.ELASTICSEARCH_CLIENT, + objects_serialized, + request_timeout=max_timeout, + ) + if errors: + logger.error( + f"Errors on sending to elastic: {errors}." + " We are not marking objects as sent." + ) + found_errors |= errors + else: + logger.info("BI sent") + self.model.objects.filter( + pk__in=objects.values_list("pk", flat=True) + ).update(sent_to_bi=True) + return found_errors + + +class CleanOnCreateQuerySet(models.QuerySet): + def create(self, **kwargs): + obj = self.model(**kwargs) + obj: models.Model + # we are forcing the clean method call. + # django rest framework DOES NOT do that by default, + # and I want to be sure that it is actually caled + obj.clean() + self._for_write = True + obj.save(force_insert=True, using=self.db) + return obj + + def many_to_many_to_array(self, field: str, field_to_save: str = None): + if not field_to_save: + field_to_save = f"{field}_array" + return self.annotate( + **{ + field_to_save: ArraySubquery( + self.model.objects.filter(pk=OuterRef("pk")).values(f"{field}__pk"), + default=Value([]), + ) + } + ) + + +class OrganizationPluginConfigurationQuerySet(models.QuerySet): + def filter_for_config(self, config_class, config_pk: str): + return self.filter( + content_type=config_class.get_content_type(), object_id=config_pk + ) + + +class AbstractConfigQuerySet(CleanOnCreateQuerySet): + def alias_disabled_in_organization(self, organization): + from api_app.models import OrganizationPluginConfiguration + + opc = OrganizationPluginConfiguration.objects.filter(organization=organization) + + return self.alias( + disabled_in_organization=Exists( + opc.filter_for_config( + config_class=self.model, config_pk=OuterRef("pk") + ).filter(disabled=True) + ) + ) + + def annotate_runnable(self, user: User = None) -> QuerySet: + # the plugin is runnable IF + # - it is not disabled + # - the user is not inside an organization that have disabled the plugin + qs = self.filter( + pk=OuterRef("pk"), + ).exclude(disabled=True) + if user and user.has_membership(): + qs = qs.alias_disabled_in_organization(user.membership.organization) + qs = qs.exclude(disabled_in_organization=True) + return self.annotate(runnable=Exists(qs)) + + +class JobQuerySet(MP_NodeQuerySet, CleanOnCreateQuerySet, SendToBiQuerySet): + def create(self, parent=None, **kwargs): + if parent: + return parent.add_child(**kwargs) + return self.model.add_root(**kwargs) + + def delete(self, *args, **kwargs): + # just to be sure to call the correct method + return MP_NodeQuerySet.delete(self, *args, **kwargs) + + @classmethod + def _get_bi_serializer_class(cls): + from api_app.serializers.job import JobBISerializer + + return JobBISerializer + + def filter_completed(self): + return self.filter(status__in=self.model.Status.final_statuses()) + + def visible_for_user(self, user: User) -> "JobQuerySet": + """ + User has access to: + - jobs with TLP = CLEAR or GREEN + - jobs with TLP = AMBER or RED and + - jobs made by ingestors if the user is an admin + created by a member of their organization. + """ + if user.has_membership(): + user_query = Q(user=user) | Q( + user__membership__organization_id=user.membership.organization_id + ) + else: + user_query = Q(user=user) + if user.is_superuser: + user_query |= Q(user__ingestors__isnull=False) + + query = Q(tlp__in=[TLP.CLEAR, TLP.GREEN]) | ( + Q(tlp__in=[TLP.AMBER, TLP.RED]) & user_query + ) + return self.filter(query) + + def _annotate_importance_date(self) -> "JobQuerySet": + # the scans in the last day get a 3x + # the scans in the last week get a 2x + + return self.annotate( + date_weight=Case( + When( + finished_analysis_time__gte=now() - datetime.timedelta(hours=24), + then=Value(3), + ), + When( + finished_analysis_time__gte=now() - datetime.timedelta(days=7), + then=Value(2), + ), + default=Value(0), + ), + ) + + def _annotate_importance_user(self, user: User) -> "JobQuerySet": + # the scans from the user get a 3x + # the scans from the same org get a 2x + user_case = Case(When(user__pk=user.pk, then=Value(3)), default=Value(0)) + if user.has_membership(): + user_case.cases.append( + When( + user__membership__organization__pk=user.membership.organization.pk, + then=Value(2), + ) + ) + + return self.annotate(user_weight=user_case) + + def annotate_importance(self, user: User) -> QuerySet: + return ( + self._annotate_importance_date() + ._annotate_importance_user(user) + .annotate(importance=F("date_weight") + F("user_weight")) + ) + + def running( + self, check_pending: bool = False, minutes_ago: int = 25 + ) -> "JobQuerySet": + qs = self.exclude( + status__in=[status.value for status in self.model.Status.final_statuses()] + ) + if not check_pending: + qs = qs.exclude(status=self.model.Status.PENDING.value) + difference = now() - datetime.timedelta(minutes=minutes_ago) + return qs.filter(received_request_time__lte=difference) + + +class ParameterQuerySet(CleanOnCreateQuerySet): + def annotate_configured( + self, config: "PythonConfig", user: User = None + ) -> "ParameterQuerySet": + from api_app.models import PluginConfig + + # A parameter it is configured for a user if + # there is a PluginConfig that is visible for the user + # If the user is None, we only retrieve default parameters + return self.annotate( + configured=Exists( + PluginConfig.objects.filter( + parameter=OuterRef("pk"), **{config.snake_case_name: config.pk} + ).visible_for_user(user) + ) + ) + + def _alias_owner_value_for_user( + self, config: "PythonConfig", user: User = None + ) -> "ParameterQuerySet": + from api_app.models import PluginConfig + + return self.alias( + owner_value=Subquery( + PluginConfig.objects.filter( + parameter__pk=OuterRef("pk"), + **{config.snake_case_name: config.pk}, + for_organization=False, + ) + .visible_for_user_owned(user) + .values("value")[:1], + ) + ) + + def _alias_org_value_for_user( + self, config: "PythonConfig", user: User = None + ) -> "ParameterQuerySet": + from api_app.models import PluginConfig + + return self.alias( + org_value=Subquery( + PluginConfig.objects.filter( + parameter__pk=OuterRef("pk"), **{config.snake_case_name: config.pk} + ) + .visible_for_user_by_org(user) + .values("value")[:1], + ) + if user and user.has_membership() + else Value(None, output_field=JSONField()), + ) + + def _alias_default_value(self, config: "PythonConfig") -> "ParameterQuerySet": + from api_app.models import PluginConfig + + return self.alias( + default_value=Subquery( + PluginConfig.objects.filter( + parameter__pk=OuterRef("pk"), **{config.snake_case_name: config.pk} + ) + .default_values() + .values("value")[:1], + ) + ) + + def _alias_runtime_config(self, runtime_config=None): + if not runtime_config: + runtime_config = {} + # we are creating conditions for when runtime config should be used + whens = [ + When(name=para, then=Value(value, output_field=JSONField())) + for para, value in runtime_config.items() + ] + return self.annotate( + runtime_value=Case(*whens, default=None, output_field=JSONField()) + ) + + def _alias_for_test(self): + if not settings.STAGE_CI and not settings.MOCK_CONNECTIONS: + return self.alias( + test_value=Value( + None, + ) + ) + return self.alias( + test_value=Case( + When( + name__icontains="url", + then=Value("https://intelowl.com", output_field=JSONField()), + ), + When( + name="pdns_credentials", + then=Value("user|pwd", output_field=JSONField()), + ), + When(name__contains="test", then=Value(None, output_field=JSONField())), + When( + type=ParamTypes.INT.value, then=Value(10, output_field=JSONField()) + ), + default=Value("test", output_field=JSONField()), + output_field=JSONField(), + ) + ) + + def annotate_value_for_user( + self, config: "PythonConfig", user: User = None, runtime_config=None + ) -> "ParameterQuerySet": + return ( + self.prefetch_related("values") + ._alias_owner_value_for_user(config, user) + ._alias_org_value_for_user(config, user) + ._alias_default_value(config) + ._alias_runtime_config(runtime_config) + ._alias_for_test() + # importance order + .annotate( + # 1. runtime + # 2. owner + # 3. organization + # 4. default value + # 5. (if TEST environment) test value + # 5. (if NOT TEST environment) None + value=Case( + When( + runtime_value__isnull=False, + then=Cast(F("runtime_value"), output_field=JSONField()), + ), + When( + owner_value__isnull=False, + then=Cast(F("owner_value"), output_field=JSONField()), + ), + When( + org_value__isnull=False, + then=Cast(F("org_value"), output_field=JSONField()), + ), + When( + default_value__isnull=False, + then=Cast(F("default_value"), output_field=JSONField()), + ), + default=Cast(F("test_value"), output_field=JSONField()), + output_field=JSONField(), + ), + is_from_org=Case( + When( + runtime_value__isnull=True, + owner_value__isnull=True, + org_value__isnull=False, + then=Value(True), + ), + default=Value(False), + output_field=BooleanField(), + ), + ) + ) + + +class AbstractReportQuerySet(SendToBiQuerySet): + def filter_completed(self): + return self.filter(status__in=self.model.Status.final_statuses()) + + def filter_retryable(self): + return self.filter( + status__in=[self.model.Status.FAILED.value, self.model.Status.PENDING.value] + ) + + def get_configurations(self) -> AbstractConfigQuerySet: + return self.model.config.objects.filter(pk__in=self.values("config__pk")) + + +class ModelWithOwnershipQuerySet: + def default_values(self): + return self.filter(owner__isnull=True) + + def visible_for_user_by_org(self, user: User): + try: + membership = Membership.objects.get(user=user) + except Membership.DoesNotExist: + return self.none() + else: + # If you are member of an organization you should see the configs. + return self.filter( + for_organization=True, + owner__membership__organization=membership.organization, + ) + + def visible_for_user_owned(self, user: User): + return self.filter(owner=user) + + def visible_for_user(self, user: User = None) -> "PluginConfigQuerySet": + if user: + # User-level custom configs should override organization-level configs, + # we need to get the organization-level configs, if any, first. + try: + membership = Membership.objects.get(user=user) + except Membership.DoesNotExist: + # If user is not a member of any organization, + # we don't need to do anything. + return self.filter(Q(owner=user) | Q(owner__isnull=True)) + else: + # If you are member of an organization you should see the configs. + return self.filter( + Q( + for_organization=True, + owner__membership__organization=membership.organization, + ) + | Q(owner=user) + | Q(owner__isnull=True) + ) + else: + return self.default_values() + + +class PluginConfigQuerySet(CleanOnCreateQuerySet, ModelWithOwnershipQuerySet): + ... + + +class PythonConfigQuerySet(AbstractConfigQuerySet): + def annotate_configured(self, user: User = None) -> "PythonConfigQuerySet": + # a Python plugin is configured only if every required parameter is configured + from api_app.models import Parameter, PluginConfig + + return ( + # we retrieve the number or required parameters + self.alias( + required_params=Coalesce( + Subquery( + Parameter.objects.filter( + python_module=OuterRef("python_module"), required=True + ) + # count them + .annotate(count=Func(F("pk"), function="Count")).values( + "count" + ), + output_field=IntegerField(), + ), + 0, + ) + ) + # how many of them are configured + .alias( + # just to be sure that if the query fails, we return an integered + required_configured_params=Coalesce( + Subquery( + # we count how many parameters have a valid value + # considering the values that the user has access to + Parameter.objects.filter( + pk__in=Subquery( + # we get all values that the user can see + PluginConfig.objects.filter( + **{ + self.model.snake_case_name: OuterRef( + OuterRef("pk") + ) + }, + parameter__required=True, + ) + .visible_for_user(user) + .values("parameter__pk") + ) + ) + .annotate(count=Func(F("pk"), function="Count")) + .values("count"), + output_field=IntegerField(), + ), + 0, + ) + ) + # and we save the difference + .annotate( + configured=Exact( + F("required_params") - F("required_configured_params"), 0 + ) + ) + ) + + def annotate_runnable(self, user: User = None) -> "PythonConfigQuerySet": + # we are excluding the plugins that has failed the health_check + qs = ( + self.exclude(health_check_status=False) + # we save the `configured` attribute in the queryset + .annotate_configured(user) + ) + return ( + # this super call parameters are required + super(PythonConfigQuerySet, qs) + # we set the parent `runnable` attribute + .annotate_runnable(user) + # and we do the logic AND between the two fields + .annotate( + runnable=Exact( + # I have no idea how to do the compare + # of two boolean field in a subquery. + # this is the same as runnable =`configured` AND `runnable` + Cast(F("configured"), IntegerField()) + * Cast(F("runnable"), IntegerField()), + 1, + ) + ) + ) + + def get_signatures(self, job) -> Generator[Signature, None, None]: + from api_app.models import AbstractReport, Job, PythonConfig + from intel_owl import tasks + + job: Job + for config in self: + config: PythonConfig + if not hasattr(config, "runnable"): + raise RuntimeError( + "You have to call `annotate_runnable`" + " before being able to call `get_signature`" + ) + # gen new task_id + if not config.runnable: + raise RuntimeWarning( + "You are trying to get the signature of a not runnable plugin" + ) + + task_id = str(uuid.uuid4()) + config.generate_empty_report( + job, task_id, AbstractReport.Status.PENDING.value + ) + args = [ + job.pk, + config.python_module_id, + config.pk, + job.get_config_runtime_configuration(config), + task_id, + ] + yield tasks.run_plugin.signature( + args, + {}, + queue=config.queue, + soft_time_limit=config.soft_time_limit, + task_id=task_id, + immutable=True, + MessageGroupId=str(task_id), + priority=job.priority, + ) + + +class IngestorQuerySet(PythonConfigQuerySet): + def annotate_runnable(self, user: User = None) -> "PythonConfigQuerySet": + # the plugin is runnable IF + # - it is not disabled + qs = self.filter( + pk=OuterRef("pk"), + ).exclude(disabled=True) + + return self.annotate(runnable=Exists(qs)) diff --git a/Submodules/IntelOwl/api_app/serializers/__init__.py b/Submodules/IntelOwl/api_app/serializers/__init__.py new file mode 100644 index 0000000..8c7419b --- /dev/null +++ b/Submodules/IntelOwl/api_app/serializers/__init__.py @@ -0,0 +1,95 @@ +from django.conf import settings +from django.utils.timezone import now +from rest_framework import serializers as rfs +from rest_framework.exceptions import ValidationError +from rest_framework.fields import Field +from rest_framework.serializers import ModelSerializer + +from api_app.interfaces import OwnershipAbstractModel +from certego_saas.apps.organization.organization import Organization + + +class AbstractBIInterface(ModelSerializer): + application = rfs.CharField(read_only=True, default="IntelOwl") + environment = rfs.SerializerMethodField(method_name="get_environment") + timestamp: Field + username: Field + name: Field + class_instance = rfs.SerializerMethodField( + read_only=True, method_name="get_class_instance" + ) + process_time: Field + status: Field + end_time: Field + + class Meta: + fields = [ + "application", + "environment", + "timestamp", + "username", + "name", + "class_instance", + "process_time", + "status", + "end_time", + ] + + def get_class_instance(self, instance): + return instance.__class__.__name__.lower() + + @staticmethod + def get_environment(instance): + if settings.STAGE_PRODUCTION: + return "prod" + elif settings.STAGE_STAGING: + return "stag" + else: + return "test" + + @staticmethod + def to_elastic_dict(data): + return { + "_source": data, + "_index": settings.ELASTICSEARCH_BI_INDEX + "-" + now().strftime("%Y.%m"), + "_op_type": "index", + } + + +class ModelWithOwnershipSerializer(rfs.ModelSerializer): + class Meta: + model = OwnershipAbstractModel + fields = ("for_organization", "owner") + abstract = True + + owner = rfs.HiddenField(default=rfs.CurrentUserDefault()) + organization = rfs.SlugRelatedField( + queryset=Organization.objects.all(), + required=False, + allow_null=True, + slug_field="name", + write_only=True, + default=None, + ) + + def validate(self, attrs): + org = attrs.pop("organization", None) + if org: + # 1 - we are owner OR + # 2 - we are admin of the same org + if org.owner == attrs["owner"] or ( + self.context["request"].user.has_membership() + and self.context["request"].user.membership.organization.pk == org.pk + and self.context["request"].user.membership.is_admin + ): + attrs["for_organization"] = True + else: + raise ValidationError( + {"detail": "You are not owner or admin of the organization"} + ) + return super().validate(attrs) + + def to_representation(self, instance: OwnershipAbstractModel): + result = super().to_representation(instance) + result["owner"] = instance.owner.username if instance.owner else None + return result diff --git a/Submodules/IntelOwl/api_app/serializers/celery.py b/Submodules/IntelOwl/api_app/serializers/celery.py new file mode 100644 index 0000000..050a292 --- /dev/null +++ b/Submodules/IntelOwl/api_app/serializers/celery.py @@ -0,0 +1,29 @@ +from django_celery_beat.models import CrontabSchedule, PeriodicTask +from rest_framework import serializers as rfs + + +class CrontabScheduleSerializer(rfs.ModelSerializer): + class Meta: + model = CrontabSchedule + fields = [ + "minute", + "hour", + "day_of_week", + "day_of_month", + "month_of_year", + ] + + +class PeriodicTaskSerializer(rfs.ModelSerializer): + crontab = CrontabScheduleSerializer(read_only=True) + + class Meta: + model = PeriodicTask + fields = [ + "crontab", + "name", + "task", + "kwargs", + "queue", + "enabled", + ] diff --git a/Submodules/IntelOwl/api_app/serializers/job.py b/Submodules/IntelOwl/api_app/serializers/job.py new file mode 100644 index 0000000..e82f50d --- /dev/null +++ b/Submodules/IntelOwl/api_app/serializers/job.py @@ -0,0 +1,1086 @@ +import copy +import datetime +import ipaddress +import logging +import re +import uuid +from typing import Dict, Generator, List, Union + +import django.core +from django.conf import settings +from django.db.models import Q, QuerySet +from django.http import QueryDict +from django.utils.timezone import now +from rest_framework import serializers as rfs +from rest_framework.exceptions import ValidationError +from rest_framework.fields import empty +from rest_framework.serializers import ModelSerializer + +from api_app.analyzers_manager.constants import ObservableTypes, TypeChoices +from api_app.analyzers_manager.models import AnalyzerConfig, MimeTypes +from api_app.choices import TLP, ScanMode +from api_app.connectors_manager.exceptions import NotRunnableConnector +from api_app.connectors_manager.models import ConnectorConfig +from api_app.defaults import default_runtime +from api_app.helpers import calculate_md5, gen_random_colorhex +from api_app.investigations_manager.models import Investigation +from api_app.models import Comment, Job, Tag +from api_app.pivots_manager.models import PivotMap +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.serializers import AbstractBIInterface +from api_app.serializers.report import AbstractReportSerializerInterface +from api_app.visualizers_manager.models import VisualizerConfig +from certego_saas.apps.organization.permissions import IsObjectOwnerOrSameOrgPermission +from certego_saas.apps.user.models import User +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +class UserSerializer(rfs.ModelSerializer): + class Meta: + model = User + fields = ("username",) + + +class TagSerializer(rfs.ModelSerializer): + class Meta: + model = Tag + fields = rfs.ALL_FIELDS + + +class JobRecentScanSerializer(rfs.ModelSerializer): + playbook = rfs.CharField( + source="playbook_to_execute.name", allow_null=True, read_only=True + ) + user = rfs.CharField(source="user.username", allow_null=False, read_only=True) + importance = rfs.IntegerField(allow_null=True, read_only=True) + + class Meta: + model = Job + fields = [ + "playbook", + "pk", + "tlp", + "user", + "importance", + "observable_name", + "file_name", + "finished_analysis_time", + ] + + +class _AbstractJobViewSerializer(rfs.ModelSerializer): + """ + Base Serializer for ``Job`` model's ``retrieve()`` and ``list()``. + """ + + user = UserSerializer() + tags = TagSerializer(many=True, read_only=True) + + +class _AbstractJobCreateSerializer(rfs.ModelSerializer): + """ + Base Serializer for ``Job`` model's ``create()``. + """ + + class Meta: + fields = ( + "id", + "user", + "is_sample", + "tlp", + "runtime_configuration", + "analyzers_requested", + "connectors_requested", + "playbook_requested", + "tags_labels", + "scan_mode", + "scan_check_time", + "investigation", + "parent_job", + ) + + md5 = rfs.HiddenField(default=None) + is_sample = rfs.HiddenField(write_only=True, default=False) + user = rfs.HiddenField(default=rfs.CurrentUserDefault()) + scan_mode = rfs.ChoiceField( + choices=ScanMode.choices, + required=False, + ) + scan_check_time = rfs.DurationField(required=False, allow_null=True) + + tags_labels = rfs.ListField( + child=rfs.CharField(required=True), default=list, required=False + ) + runtime_configuration = rfs.JSONField(required=False, write_only=True) + tlp = rfs.ChoiceField(choices=TLP.values + ["WHITE"], required=False) + investigation = rfs.PrimaryKeyRelatedField( + queryset=Investigation.objects.all(), many=False, required=False, default=None + ) + parent_job = rfs.PrimaryKeyRelatedField(queryset=Job.objects.all(), required=False) + connectors_requested = rfs.SlugRelatedField( + slug_field="name", + queryset=ConnectorConfig.objects.all(), + many=True, + default=ConnectorConfig.objects.none(), + ) + analyzers_requested = rfs.SlugRelatedField( + slug_field="name", + queryset=AnalyzerConfig.objects.all(), + many=True, + default=AnalyzerConfig.objects.none(), + ) + playbook_requested = rfs.SlugRelatedField( + slug_field="name", + queryset=PlaybookConfig.objects.all(), + many=False, + required=False, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.filter_warnings = [] + + def validate_runtime_configuration(self, runtime_config: Dict): # skipcq: PYL-R0201 + from api_app.validators import validate_runtime_configuration + + if not runtime_config: + runtime_config = default_runtime() + try: + validate_runtime_configuration(runtime_config) + except django.core.exceptions.ValidationError as e: + logger.info(e, stack_info=True) + raise ValidationError({"detail": "Runtime Configuration Validation Failed"}) + return runtime_config + + def validate_tags_labels(self, tags_labels): # skipcq: PYL-R0201 + for label in tags_labels: + yield Tag.objects.get_or_create( + label=label, defaults={"color": gen_random_colorhex()} + )[0] + + def validate_tlp(self, tlp: str): # skipcq: PYL-R0201 + if tlp == "WHITE": + return TLP.CLEAR.value + return tlp + + def run_validation(self, data=empty): + result = super().run_validation(data=data) + self.filter_warnings.clear() + return result + + @staticmethod + def set_default_value_from_playbook(attrs: Dict) -> None: + playbook = attrs["playbook_requested"] + # we are changing attrs in place + for attribute in [ + "scan_mode", + "scan_check_time", + "tlp", + "runtime_configuration", + ]: + if attribute not in attrs: + attrs[attribute] = getattr(playbook, attribute) + + def validate_investigation(self, investigation: Investigation = None): + if investigation and not investigation.user_can_edit( + self.context["request"].user + ): + raise ValidationError( + {"detail": "You can't create a job to this investigation"} + ) + return investigation + + def validate(self, attrs: dict) -> dict: + if attrs.get("playbook_requested"): + self.set_default_value_from_playbook(attrs) + # this TLP validation must be after the Playbook checks to avoid + # to overwrite the Playbook default TLP + if "tlp" not in attrs: + attrs["tlp"] = TLP.CLEAR.value + if "scan_mode" not in attrs: + attrs["scan_mode"] = ScanMode.CHECK_PREVIOUS_ANALYSIS.value + if attrs.get( + "scan_mode" + ) == ScanMode.CHECK_PREVIOUS_ANALYSIS.value and not attrs.get( + "scan_check_time" + ): + attrs["scan_check_time"] = datetime.timedelta(hours=24) + elif attrs.get("scan_mode") == ScanMode.FORCE_NEW_ANALYSIS.value: + attrs["scan_check_time"] = None + attrs = super().validate(attrs) + if playbook := attrs.get("playbook_requested", None): + playbook: PlaybookConfig + if attrs.get("analyzers_requested", []) or attrs.get( + "connectors_requested", [] + ): + raise ValidationError( + {"detail": "You can't specify a playbook and plugins together"} + ) + if playbook.disabled: + raise ValidationError( + {"detail": "No playbooks can be run after filtering."} + ) + attrs["playbook_to_execute"] = playbook + attrs["analyzers_requested"] = list(playbook.analyzers.all()) + attrs["connectors_requested"] = list(playbook.connectors.all()) + attrs["tags_labels"] = list(attrs.get("tags_labels", [])) + list( + playbook.tags.all() + ) + + analyzers_to_execute = attrs[ + "analyzers_to_execute" + ] = self.set_analyzers_to_execute(**attrs) + connectors_to_execute = attrs[ + "connectors_to_execute" + ] = self.set_connectors_to_execute(**attrs) + if not analyzers_to_execute and not connectors_to_execute: + warnings = "\n".join(self.filter_warnings) + raise ValidationError( + { + "detail": "No Analyzers and Connectors " + f"can be run after filtering:\n{warnings}" + } + ) + + attrs["visualizers_to_execute"] = self.set_visualizers_to_execute(**attrs) + attrs["warnings"] = list(self.filter_warnings) + attrs["tags"] = attrs.pop("tags_labels", []) + return attrs + + def set_visualizers_to_execute( + self, + tlp: str, + playbook_requested: PlaybookConfig = None, + **kwargs, + ) -> List[VisualizerConfig]: + if playbook_requested: + visualizers = VisualizerConfig.objects.filter( + playbooks__in=[playbook_requested], disabled=False + ) + else: + visualizers = [] + return list(self.plugins_to_execute(tlp, visualizers)) + + def set_connectors_to_execute( + self, connectors_requested: List[ConnectorConfig], tlp: str, **kwargs + ) -> List[ConnectorConfig]: + return list(self.plugins_to_execute(tlp, connectors_requested)) + + def set_analyzers_to_execute( + self, analyzers_requested: List[AnalyzerConfig], tlp: str, **kwargs + ) -> List[AnalyzerConfig]: + analyzers_executed = list(self.plugins_to_execute(tlp, analyzers_requested)) + return analyzers_executed + + def plugins_to_execute( + self, + tlp, + plugins_requested: Union[ + List[Union[AnalyzerConfig, ConnectorConfig, VisualizerConfig]], QuerySet + ], + ) -> Generator[ + Union[AnalyzerConfig, ConnectorConfig, VisualizerConfig], None, None + ]: + if not plugins_requested: + return + if isinstance(plugins_requested, QuerySet): + qs = plugins_requested + else: + qs = plugins_requested[0].__class__.objects.filter( + pk__in=[plugin.pk for plugin in plugins_requested] + ) + for plugin_config in qs.annotate_runnable(self.context["request"].user): + try: + if not plugin_config.runnable: + raise NotRunnableConnector( + f"{plugin_config.name} won't run: is disabled or not configured" + ) + try: + if ( + TLP[tlp] > TLP[plugin_config.maximum_tlp] + ): # check if job's tlp allows running + # e.g. if connector_tlp is GREEN(1), + # run for job_tlp CLEAR(0) & GREEN(1) only + raise NotRunnableConnector( + f"{plugin_config.name} won't run because " + f"job.tlp is '{tlp}') while plugin" + f" maximum_tlp ('{plugin_config.maximum_tlp}')" + ) + except AttributeError: + # in case the plugin does not have maximum_tlp: + pass + except NotRunnableConnector as e: + self.filter_warnings.append(str(e)) + logger.info(e) + else: + yield plugin_config + + def check_previous_jobs(self, validated_data: Dict) -> Job: + logger.info("Checking previous jobs") + if not validated_data["scan_check_time"]: + raise ValidationError({"detail": "Scan check time can't be null"}) + status_to_exclude = [Job.Status.KILLED, Job.Status.FAILED] + if not validated_data.get("playbook_to_execute", None): + status_to_exclude.append(Job.Status.REPORTED_WITH_FAILS) + qs = ( + self.Meta.model.objects.visible_for_user(self.context["request"].user) + .filter( + received_request_time__gte=now() - validated_data["scan_check_time"] + ) + .filter(Q(md5=validated_data["md5"])) + ) + for analyzer in validated_data.get("analyzers_to_execute", []): + qs = qs.filter(analyzers_requested__in=[analyzer]) + for connector in validated_data.get("connectors_to_execute", []): + qs = qs.filter(connectors_requested__in=[connector]) + for visualizer in validated_data.get("visualizers_to_execute", []): + qs = qs.filter(visualizers_to_execute__in=[visualizer]) + + return qs.exclude(status__in=status_to_exclude).latest("received_request_time") + + def create(self, validated_data: Dict) -> Job: + warnings = validated_data.pop("warnings") + send_task = validated_data.pop("send_task", False) + parent_job = validated_data.pop("parent_job", None) + if validated_data["scan_mode"] == ScanMode.CHECK_PREVIOUS_ANALYSIS.value: + try: + return self.check_previous_jobs(validated_data) + except self.Meta.model.DoesNotExist: + job = super().create(validated_data) + else: + job = super().create(validated_data) + job.warnings = warnings + job.save() + logger.info(f"Job {job.pk} created") + if parent_job: + PivotMap.objects.create( + starting_job=validated_data["parent"], ending_job=job, pivot_config=None + ) + if send_task: + from intel_owl.tasks import job_pipeline + + logger.info(f"Sending task for job {job.pk}") + job_pipeline.apply_async( + args=[job.pk], + queue=get_queue_name(settings.DEFAULT_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=job.priority, + ) + + return job + + +class CommentSerializer(rfs.ModelSerializer): + """ + Used for ``create()`` + """ + + class Meta: + model = Comment + fields = ("id", "content", "created_at", "user", "job_id") + + user = UserSerializer(read_only=True) + job_id = rfs.PrimaryKeyRelatedField( + queryset=Job.objects.all(), write_only=True, source="job" + ) + + def validate(self, attrs: dict) -> dict: + attrs = super().validate(attrs) + + user = self.context["request"].user + job = attrs.get("job") + try: + Job.objects.visible_for_user(user).get(pk=job.pk) + except Job.DoesNotExist: + raise ValidationError( + {"detail": f"You have no permission to comment on job {job.pk}"} + ) + return attrs + + def create(self, validated_data: dict) -> Comment: + validated_data["user"] = self.context["request"].user + return super().create(validated_data) + + +class JobListSerializer(_AbstractJobViewSerializer): + """ + Used for ``list()``. + """ + + class Meta: + model = Job + exclude = ( + "file", + "errors", + "scan_mode", + "scan_check_time", + "runtime_configuration", + "sent_to_bi", + "warnings", + "analyzers_requested", + "connectors_requested", + "path", + "numchild", + "depth", + ) + + pivots_to_execute = rfs.SerializerMethodField(read_only=True) + analyzers_to_execute = rfs.SlugRelatedField( + read_only=True, slug_field="name", many=True + ) + connectors_to_execute = rfs.SlugRelatedField( + read_only=True, slug_field="name", many=True + ) + visualizers_to_execute = rfs.SlugRelatedField( + read_only=True, slug_field="name", many=True + ) + playbook_to_execute = rfs.SlugRelatedField(read_only=True, slug_field="name") + + def get_pivots_to_execute(self, obj: Job): # skipcq: PYL-R0201 + return obj.pivots_to_execute.all().values_list("name", flat=True) + + +class JobTreeSerializer(ModelSerializer): + pivot_config = rfs.CharField( + source="pivot_parent.pivot_config.name", allow_null=True, read_only=True + ) + + class Meta: + model = Job + fields = [ + "pk", + "analyzed_object_name", + "pivot_config", + "playbook", + "status", + "received_request_time", + ] + + playbook = rfs.SlugRelatedField( + source="playbook_to_execute", + slug_field="name", + queryset=PlaybookConfig.objects.all(), + many=False, + required=False, + ) + + def to_representation(self, instance): + instance: Job + data = super().to_representation(instance) + for child in instance.get_children(): + # recursive call + data.setdefault("children", []).append(self.__class__(instance=child).data) + if data["pivot_config"] is None: + del data["pivot_config"] + return data + + +class JobSerializer(_AbstractJobViewSerializer): + """ + Used for ``retrieve()`` + """ + + class Meta: + model = Job + exclude = ( + "file", + "depth", + "path", + "numchild", + "sent_to_bi", + ) + + comments = CommentSerializer(many=True, read_only=True) + pivots_to_execute = rfs.SlugRelatedField( + many=True, read_only=True, slug_field="name" + ) + analyzers_to_execute = rfs.SlugRelatedField( + many=True, read_only=True, slug_field="name" + ) + analyzers_requested = rfs.SlugRelatedField( + many=True, read_only=True, slug_field="name" + ) + connectors_to_execute = rfs.SlugRelatedField( + many=True, read_only=True, slug_field="name" + ) + connectors_requested = rfs.SlugRelatedField( + many=True, read_only=True, slug_field="name" + ) + visualizers_to_execute = rfs.SlugRelatedField( + many=True, read_only=True, slug_field="name" + ) + playbook_requested = rfs.SlugRelatedField(read_only=True, slug_field="name") + playbook_to_execute = rfs.SlugRelatedField(read_only=True, slug_field="name") + investigation = rfs.SerializerMethodField(read_only=True, default=None) + permissions = rfs.SerializerMethodField() + + def get_pivots_to_execute(self, obj: Job): # skipcq: PYL-R0201 + # this cast is required or serializer doesn't work with websocket + return list(obj.pivots_to_execute.all().values_list("name", flat=True)) + + def get_investigation(self, instance: Job): # skipcq: PYL-R0201 + if root_investigation := instance.get_root().investigation: + return root_investigation.pk + return instance.investigation + + def get_fields(self): + # this method override is required for a cyclic import + from api_app.analyzers_manager.serializers import AnalyzerReportSerializer + from api_app.connectors_manager.serializers import ConnectorReportSerializer + from api_app.pivots_manager.serializers import PivotReportSerializer + from api_app.visualizers_manager.serializers import VisualizerReportSerializer + + for field, serializer in [ + ("analyzer", AnalyzerReportSerializer), + ("connector", ConnectorReportSerializer), + ("pivot", PivotReportSerializer), + ("visualizer", VisualizerReportSerializer), + ]: + self._declared_fields[f"{field}_reports"] = serializer( + many=True, read_only=True, source=f"{field}reports" + ) + return super().get_fields() + + +class RestJobSerializer(JobSerializer): + def get_permissions(self, obj: Job) -> Dict[str, bool]: + request = self.context.get("request", None) + view = self.context.get("view", None) + has_perm = False + if request and view: + has_perm = IsObjectOwnerOrSameOrgPermission().has_object_permission( + request, view, obj + ) + return { + "kill": has_perm, + "delete": has_perm, + "plugin_actions": has_perm, + } + + +class WsJobSerializer(JobSerializer): + def get_permissions(self, obj: Job) -> Dict[str, bool]: + has_perm = self.context.get("permissions", False) + return { + "kill": has_perm, + "delete": has_perm, + "plugin_actions": has_perm, + } + + +class MultipleJobSerializer(rfs.ListSerializer): + def update(self, instance, validated_data): + raise NotImplementedError("This serializer does not support update().") + + def save(self, parent: Job = None, **kwargs): + jobs = super().save(**kwargs, parent=parent) + if parent: + # the parent has already an investigation + # so we don't need to do anything because everything is already connected + root = parent.get_root() + if root.investigation: + root.investigation.status = root.investigation.Status.RUNNING.value + root.investigation.save() + return jobs + # if we have a parent, it means we are pivoting from one job to another + else: + if parent.playbook_to_execute: + investigation_name = ( + f"{parent.playbook_to_execute.name}:" + f" {parent.analyzed_object_name}" + ) + else: + investigation_name = ( + f"Pivot investigation: {parent.analyzed_object_name}" + ) + + investigation = Investigation.objects.create( + name=investigation_name, + owner=self.context["request"].user, + ) + investigation.jobs.add(parent) + investigation.start_time = parent.received_request_time + else: + # if we do not have a parent but we have an investigation + # set investigation into running status + if len(jobs) >= 1 and jobs[0].investigation: + investigation = jobs[0].investigation + investigation.status = investigation.Status.RUNNING.value + investigation.save() + return jobs + # if we do not have a parent or an investigation, and we have multiple jobs, + # we are in the multiple input case + elif len(jobs) > 1: + investigation = Investigation.objects.create( + name=f"Custom investigation: {len(jobs)} jobs", + owner=self.context["request"].user, + ) + for job in jobs: + job: Job + job.investigation = investigation + job.save() + investigation.start_time = now() + else: + return jobs + investigation: Investigation + investigation.status = investigation.Status.RUNNING.value + investigation.for_organization = True + investigation.save() + return jobs + + def validate(self, attrs: dict) -> dict: + attrs = super().validate(attrs) + # filter requests with more elements than this threshold + max_element_per_request_number = 200 + if len(attrs) > max_element_per_request_number: + raise ValidationError( + { + "detail": "Exceed the threshold of " + f"{max_element_per_request_number} elements for a single analysis" + } + ) + return attrs + + +class MultipleFileJobSerializer(MultipleJobSerializer): + """ + ``Job`` model's serializer for Multiple File Analysis. + Used for ``create()``. + """ + + def update(self, instance, validated_data): + raise NotImplementedError("This serializer does not support update().") + + files = rfs.ListField(child=rfs.FileField(required=True), required=True) + + file_names = rfs.ListField(child=rfs.CharField(required=True), required=True) + + file_mimetypes = rfs.ListField( + child=rfs.CharField(required=True, allow_blank=True), required=False + ) + + def to_internal_value(self, data: QueryDict): + ret = [] + errors = [] + if not isinstance(data, QueryDict): + data_to_check = QueryDict(mutable=True) + data_to_check.update(data) + else: + data_to_check = data + if data_to_check.getlist("file_names", []) and len( + data_to_check.getlist("file_names") + ) != len(data_to_check.getlist("files")): + raise ValidationError( + {"detail": "file_names and files must have the same length."} + ) + + for index, file in enumerate(data_to_check.getlist("files")): + # `deepcopy` here ensures that this code doesn't + # break even if new fields are added in future + item = data_to_check.copy() + + item["file"] = file + if data_to_check.getlist("file_names", []): + item["file_name"] = data_to_check.getlist("file_names")[index] + if data_to_check.get("file_mimetypes", []): + item["file_mimetype"] = data_to_check["file_mimetypes"][index] + try: + validated = self.child.run_validation(item) + except ValidationError as exc: + errors.append(exc.detail) + else: + ret.append(validated) + + if any(errors): + raise ValidationError({"detail": errors}) + + return ret + + +class FileJobSerializer(_AbstractJobCreateSerializer): + """ + ``Job`` model's serializer for File Analysis. + Used for ``create()``. + """ + + file = rfs.FileField(required=True) + file_name = rfs.CharField(required=False) + file_mimetype = rfs.CharField(required=False) + is_sample = rfs.HiddenField(write_only=True, default=True) + + class Meta: + model = Job + fields = _AbstractJobCreateSerializer.Meta.fields + ( + "file", + "file_name", + "file_mimetype", + ) + list_serializer_class = MultipleFileJobSerializer + + def validate(self, attrs: dict) -> dict: + logger.debug(f"before attrs: {attrs}") + # calculate ``file_mimetype`` + if "file_name" not in attrs: + attrs["file_name"] = attrs["file"].name + attrs["file_mimetype"] = MimeTypes.calculate(attrs["file"], attrs["file_name"]) + # calculate ``md5`` + file_obj = attrs["file"].file + file_obj.seek(0) + file_buffer = file_obj.read() + attrs["md5"] = calculate_md5(file_buffer) + attrs = super().validate(attrs) + logger.debug(f"after attrs: {attrs}") + return attrs + + def set_analyzers_to_execute( + self, + analyzers_requested: List[AnalyzerConfig], + tlp: str, + file_mimetype: str, + file_name: str, + **kwargs, + ) -> List[AnalyzerConfig]: + analyzers_to_execute = analyzers_requested.copy() + + # get values from serializer + partially_filtered_analyzers_qs = AnalyzerConfig.objects.filter( + pk__in=[config.pk for config in analyzers_to_execute] + ) + if file_mimetype in [MimeTypes.ZIP1.value, MimeTypes.ZIP1.value]: + EXCEL_OFFICE_FILES = r"\.[xl]\w{0,3}$" + DOC_OFFICE_FILES = r"\.[doc]\w{0,3}$" + if re.search(DOC_OFFICE_FILES, file_name): + # its an excel file + file_mimetype = MimeTypes.DOC.value + elif re.search(EXCEL_OFFICE_FILES, file_name): + # its an excel file + file_mimetype = MimeTypes.EXCEL1.value + else: + # its an android file + file_mimetype = MimeTypes.APK.value + + supported_query = ( + Q( + supported_filetypes__len=0, + ) + & ~Q(not_supported_filetypes__contains=[file_mimetype]) + ) | Q(supported_filetypes__contains=[file_mimetype]) + + for analyzer in partially_filtered_analyzers_qs.exclude( + Q(type=TypeChoices.FILE) & supported_query + ): + analyzers_to_execute.remove(analyzer) + message = ( + f"{analyzer.name} won't be run " + "because does not support the file mimetype." + ) + logger.info(message) + self.filter_warnings.append(message) + return super().set_analyzers_to_execute(analyzers_to_execute, tlp) + + +class MultipleObservableJobSerializer(MultipleJobSerializer): + """ + ``Job`` model's serializer for Multiple Observable Analysis. + Used for ``create()``. + """ + + observables = rfs.ListField(required=True) + + def update(self, instance, validated_data): + raise NotImplementedError("This serializer does not support update().") + + def to_internal_value(self, data): + ret = [] + errors = [] + observables = data.pop("observables", []) + # TODO we could change the signature, but this means change frontend + clients + for _, name in observables: + # `deepcopy` here ensures that this code doesn't + # break even if new fields are added in future + item = copy.deepcopy(data) + item["observable_name"] = name + try: + validated = self.child.run_validation(item) + except ValidationError as exc: + errors.append(exc.detail) + else: + ret.append(validated) + data["observables"] = observables + if any(errors): + raise ValidationError({"detail": errors}) + return ret + + +class ObservableAnalysisSerializer(_AbstractJobCreateSerializer): + """ + ``Job`` model's serializer for Observable Analysis. + Used for ``create()``. + """ + + observable_name = rfs.CharField(required=True) + observable_classification = rfs.CharField(required=False) + is_sample = rfs.HiddenField(write_only=True, default=False) + + class Meta: + model = Job + fields = _AbstractJobCreateSerializer.Meta.fields + ( + "observable_name", + "observable_classification", + ) + list_serializer_class = MultipleObservableJobSerializer + + def validate(self, attrs: dict) -> dict: + logger.debug(f"before attrs: {attrs}") + attrs["observable_name"] = self.defanged_values_removal( + attrs["observable_name"] + ) + # calculate ``observable_classification`` + if not attrs.get("observable_classification", None): + attrs["observable_classification"] = ObservableTypes.calculate( + attrs["observable_name"] + ) + if attrs["observable_classification"] in [ + ObservableTypes.HASH, + ObservableTypes.DOMAIN, + ]: + # force lowercase in ``observable_name``. + # Ref: https://github.com/intelowlproject/IntelOwl/issues/658 + attrs["observable_name"] = attrs["observable_name"].lower() + + if attrs["observable_classification"] == ObservableTypes.IP.value: + ip = ipaddress.ip_address(attrs["observable_name"]) + if ip.is_loopback: + raise ValidationError({"detail": "Loopback address"}) + elif ip.is_private: + raise ValidationError({"detail": "Private address"}) + elif ip.is_multicast: + raise ValidationError({"detail": "Multicast address"}) + elif ip.is_link_local: + raise ValidationError({"detail": "Local link address"}) + elif ip.is_reserved: + raise ValidationError({"detail": "Reserved address"}) + + # calculate ``md5`` + attrs["md5"] = calculate_md5(attrs["observable_name"].encode("utf-8")) + attrs = super().validate(attrs) + logger.debug(f"after attrs: {attrs}") + return attrs + + @staticmethod + def defanged_values_removal(value): + if "\\" in value: + value = value.replace("\\", "") + if "]" in value: + value = value.replace("]", "") + if "[" in value: + value = value.replace("[", "") + # this is a trick done by spammers + if "\n" in value: + value = value.replace("\n", "") + return value + + def set_analyzers_to_execute( + self, + analyzers_requested: List[AnalyzerConfig], + tlp: str, + observable_classification: str, + **kwargs, + ) -> List[AnalyzerConfig]: + analyzers_to_execute = analyzers_requested.copy() + + partially_filtered_analyzers_qs = AnalyzerConfig.objects.filter( + pk__in=[config.pk for config in analyzers_to_execute] + ) + for analyzer in partially_filtered_analyzers_qs.exclude( + type=TypeChoices.OBSERVABLE, + observable_supported__contains=[observable_classification], + ): + analyzers_to_execute.remove(analyzer) + message = ( + f"{analyzer.name} won't be run because" + " it does not support the requested observable." + ) + logger.info(message) + self.filter_warnings.append(message) + + return super().set_analyzers_to_execute(analyzers_to_execute, tlp) + + +class JobEnvelopeSerializer(rfs.ListSerializer): + @property + def data(self): + # this is to return a dict instead of a list + return super(rfs.ListSerializer, self).data + + def to_internal_value(self, data): + super().to_internal_value(data) + + def to_representation(self, data): + results = super().to_representation(data) + return {"results": results, "count": len(results)} + + +class JobResponseSerializer(rfs.ModelSerializer): + STATUS_ACCEPTED = "accepted" + STATUS_NOT_AVAILABLE = "not_available" + + job_id = rfs.IntegerField(source="pk") + analyzers_running = rfs.SlugRelatedField( + read_only=True, + source="analyzers_to_execute", + many=True, + slug_field="name", + ) + connectors_running = rfs.SlugRelatedField( + read_only=True, + source="connectors_to_execute", + many=True, + slug_field="name", + ) + visualizers_running = rfs.SlugRelatedField( + read_only=True, + source="visualizers_to_execute", + many=True, + slug_field="name", + ) + playbook_running = rfs.SlugRelatedField( + read_only=True, + source="playbook_to_execute", + slug_field="name", + ) + investigation = rfs.SerializerMethodField(read_only=True, default=None) + + class Meta: + model = Job + fields = [ + "job_id", + "analyzers_running", + "connectors_running", + "visualizers_running", + "playbook_running", + "investigation", + ] + extra_kwargs = {"warnings": {"read_only": True, "required": False}} + list_serializer_class = JobEnvelopeSerializer + + def get_investigation(self, instance: Job): # skipcq: PYL-R0201 + if root_investigation := instance.get_root().investigation: + return root_investigation.pk + return instance.investigation + + def to_representation(self, instance: Job): + result = super().to_representation(instance) + result["status"] = self.STATUS_ACCEPTED + result["already_exists"] = bool( + instance.status in instance.Status.final_statuses() + ) + return result + + def get_initial(self): + initial = super().get_initial() + initial.setdefault("status", self.STATUS_NOT_AVAILABLE) + return initial + + +class JobAvailabilitySerializer(rfs.ModelSerializer): + """ + Serializer for ask_analysis_availability + """ + + class Meta: + model = Job + fields = ["md5", "analyzers", "playbooks", "running_only", "minutes_ago"] + + md5 = rfs.CharField(max_length=128, required=True) + analyzers = rfs.SlugRelatedField( + queryset=AnalyzerConfig.objects.all(), + many=True, + required=False, + slug_field="name", + ) + playbooks = rfs.SlugRelatedField( + queryset=PlaybookConfig.objects.all(), + required=False, + many=True, + slug_field="name", + ) + running_only = rfs.BooleanField(default=False, required=False) + minutes_ago = rfs.IntegerField(default=None, required=False) + + def validate(self, attrs): + attrs = super().validate(attrs) + playbooks = attrs.get("playbooks", []) + analyzers = attrs.get("analyzers", []) + if not analyzers and not playbooks: + attrs["analyzers"] = list(AnalyzerConfig.objects.all()) + elif len(playbooks) != 0 and len(analyzers) != 0: + raise rfs.ValidationError( + "Either only send the 'playbooks' parameter or the 'analyzers' one." + ) + return attrs + + def create(self, validated_data): + statuses_to_check = [Job.Status.RUNNING] + + if not validated_data["running_only"]: + statuses_to_check.append(Job.Status.REPORTED_WITHOUT_FAILS) + # since with playbook + # it is expected behavior + # for analyzers to often fail + if validated_data.get("playbooks", []): + statuses_to_check.append(Job.Status.REPORTED_WITH_FAILS) + # this means that the user is trying to + # check availability of the case where all + # analyzers were run but no playbooks were + # triggered. + query = Q(md5=validated_data["md5"]) & Q(status__in=statuses_to_check) + if validated_data.get("playbooks", []): + query &= Q(playbook_requested__name__in=validated_data["playbooks"]) + else: + analyzers = validated_data.get("analyzers", []) + for analyzer in analyzers: + query &= Q(analyzers_requested__name__in=[analyzer]) + # we want a job that has every analyzer requested + if validated_data.get("minutes_ago", None): + minutes_ago_time = now() - datetime.timedelta( + minutes=validated_data["minutes_ago"] + ) + query &= Q(received_request_time__gte=minutes_ago_time) + + last_job_for_md5 = ( + Job.objects.visible_for_user(self.context["request"].user) + .filter(query) + .only("pk") + .latest("received_request_time") + ) + return last_job_for_md5 + + +class JobBISerializer(AbstractBIInterface, ModelSerializer): + timestamp = rfs.DateTimeField(source="received_request_time") + username = rfs.CharField(source="user.username") + name = rfs.CharField(source="pk") + end_time = rfs.DateTimeField(source="finished_analysis_time") + playbook = rfs.SerializerMethodField(source="get_playbook") + + class Meta: + model = Job + fields = AbstractBIInterface.Meta.fields + [ + "playbook", + "runtime_configuration", + "is_sample", + ] + list_serializer_class = ( + AbstractReportSerializerInterface.Meta.list_serializer_class + ) + + def to_representation(self, instance: Job): + data = super().to_representation(instance) + return self.to_elastic_dict(data) + + @staticmethod + def get_playbook(instance: Job): + return instance.playbook_to_execute.name if instance.playbook_to_execute else "" diff --git a/Submodules/IntelOwl/api_app/serializers/plugin.py b/Submodules/IntelOwl/api_app/serializers/plugin.py new file mode 100644 index 0000000..9b8c310 --- /dev/null +++ b/Submodules/IntelOwl/api_app/serializers/plugin.py @@ -0,0 +1,305 @@ +import json +import logging +from typing import Any + +from django.core.cache import cache +from rest_framework import serializers as rfs +from rest_framework.exceptions import ValidationError +from rest_framework.fields import SerializerMethodField + +from api_app.analyzers_manager.models import AnalyzerConfig +from api_app.connectors_manager.models import ConnectorConfig +from api_app.ingestors_manager.models import IngestorConfig +from api_app.models import Parameter, PluginConfig, PythonConfig, PythonModule +from api_app.serializers import ModelWithOwnershipSerializer +from api_app.serializers.celery import CrontabScheduleSerializer +from api_app.visualizers_manager.models import VisualizerConfig +from certego_saas.apps.user.models import User + +logger = logging.getLogger(__name__) + + +class PluginConfigSerializer(ModelWithOwnershipSerializer): + class Meta: + model = PluginConfig + fields = ( + "attribute", + "config_type", + "type", + "plugin_name", + "value", + "owner", + "organization", + "id", + ) + + class CustomValueField(rfs.JSONField): + def to_internal_value(self, data): + if not data: + raise ValidationError({"detail": "Empty insertion"}) + logger.info(f"verifying that value {data} ({type(data)}) is JSON compliant") + try: + return json.loads(data) + except json.JSONDecodeError: + try: + data = json.dumps(data) + return json.loads(data) + except json.JSONDecodeError: + logger.info(f"value {data} ({type(data)}) raised ValidationError") + raise ValidationError({"detail": "Value is not JSON-compliant."}) + + def get_attribute(self, instance: PluginConfig): + # We return `redacted` when + # 1) is a secret AND + # 2) is a value for the organization AND + # (NOR OPERATOR) + # 3) we are not its owner OR + # 4) we are not an admin of the same organization + if ( + instance.is_secret() + and instance.for_organization + and not ( + self.context["request"].user.pk == instance.owner.pk + or ( + self.context["request"].user.has_membership() + and self.context["request"].user.membership.organization.pk + == instance.owner.membership.organization.pk + and self.context["request"].user.membership.is_admin + ) + ) + ): + return "redacted" + return super().get_attribute(instance) + + def to_representation(self, value): + result = super().to_representation(value) + if isinstance(result, (list, dict)): + return json.dumps(result) + return result + + type = rfs.ChoiceField(choices=["1", "2", "3", "4"]) # retrocompatibility + config_type = rfs.ChoiceField(choices=["1", "2"]) # retrocompatibility + attribute = rfs.CharField() + plugin_name = rfs.CharField() + value = CustomValueField() + + def validate_value_type(self, value: Any, parameter: Parameter): + if type(value).__name__ != parameter.type: + raise ValidationError( + { + "detail": f"Value has type {type(value).__name__}" + f" instead of {parameter.type}" + } + ) + + def validate(self, attrs): + if self.partial: + # we are in an update + return attrs + _value = attrs["value"] + # retro compatibility + _type = attrs.pop("type") + _config_type = attrs.pop("config_type") + _plugin_name = attrs.pop("plugin_name") + _attribute = attrs.pop("attribute") + if _type == "1": + class_ = AnalyzerConfig + elif _type == "2": + class_ = ConnectorConfig + elif _type == "3": + class_ = VisualizerConfig + elif _type == "4": + class_ = IngestorConfig + else: + raise RuntimeError("Not configured") + # we set the pointers allowing retro-compatibility from the frontend + config = class_.objects.get(name=_plugin_name) + parameter = config.parameters.get( + name=_attribute, is_secret=_config_type == "2" + ) + self.validate_value_type(_value, parameter) + + attrs["parameter"] = parameter + attrs[class_.snake_case_name] = config + return super().validate(attrs) + + def update(self, instance, validated_data): + self.validate_value_type(validated_data["value"], instance.parameter) + return super().update(instance, validated_data) + + def to_representation(self, instance: PluginConfig): + result = super().to_representation(instance) + result["organization"] = ( + instance.organization.name if instance.organization is not None else None + ) + return result + + +class ParamListSerializer(rfs.ListSerializer): + @property + def data(self): + # this is to return a dict instead of a list + return super(rfs.ListSerializer, self).data + + def to_representation(self, data): + result = super().to_representation(data) + return {elem.pop("name"): elem for elem in result} + + +class ParameterSerializer(rfs.ModelSerializer): + value = SerializerMethodField() + + class Meta: + model = Parameter + fields = ["name", "type", "description", "required", "value", "is_secret"] + list_serializer_class = ParamListSerializer + + def get_value(self, param: Parameter): + if hasattr(param, "value") and hasattr(param, "is_from_org"): + if param.is_secret and param.is_from_org: + return "redacted" + return param.value + + +class PythonConfigListSerializer(rfs.ListSerializer): + plugins = rfs.PrimaryKeyRelatedField(read_only=True) + + def to_representation_single_plugin(self, plugin: PythonConfig, user: User): + cache_name = ( + f"serializer_{plugin.__class__.__name__}_{plugin.name}_{user.username}" + ) + cache_hit = cache.get(cache_name) + if not cache_hit: + plugin_representation = self.child.to_representation(plugin) + plugin_representation["secrets"] = {} + plugin_representation["params"] = {} + total_parameters = 0 + parameter_required_not_configured = [] + for param in plugin.python_module.parameters.annotate_configured( + plugin, user + ).annotate_value_for_user(plugin, user): + total_parameters += 1 + if param.required and not param.configured: + parameter_required_not_configured.append(param.name) + param_representation = ParameterSerializer(param).data + param_representation.pop("name") + key = "secrets" if param.is_secret else "params" + + plugin_representation[key][param.name] = param_representation + + if not parameter_required_not_configured: + logger.debug(f"Plugin {plugin.name} is configured") + configured = True + details = "Ready to use!" + else: + logger.debug(f"Plugin {plugin.name} is not configured") + details = ( + f"{', '.join(parameter_required_not_configured)} " + "secret" + f"{'' if len(parameter_required_not_configured) == 1 else 's'}" + " not set;" + f" ({total_parameters - len(parameter_required_not_configured)} " + f"of {total_parameters} satisfied)" + ) + configured = False + plugin_representation["disabled"] = not plugin.enabled_for_user(user) + plugin_representation["verification"] = { + "configured": configured, + "details": details, + "missing_secrets": parameter_required_not_configured, + } + logger.info(f"Setting cache {cache_name}") + cache.set(cache_name, plugin_representation, timeout=60 * 60 * 24 * 7) + return plugin_representation + else: + cache.touch(cache_name, timeout=60 * 60 * 24 * 7) + return cache_hit + + def to_representation(self, data): + user = self.context["request"].user + for plugin in data: + yield self.to_representation_single_plugin(plugin, user) + + +class PythonModulSerializerComplete(rfs.ModelSerializer): + health_check_schedule = CrontabScheduleSerializer() + update_schedule = CrontabScheduleSerializer() + + class Meta: + model = PythonModule + exclude = ["id", "update_task"] + + +class PythonModuleSerializer(rfs.ModelSerializer): + class Meta: + model = PythonModule + fields = ["module", "base_path"] + + +class ParameterCompleteSerializer(rfs.ModelSerializer): + python_module = PythonModuleSerializer(read_only=True) + + class Meta: + model = Parameter + exclude = ["id"] + + +class PluginConfigCompleteSerializer(rfs.ModelSerializer): + parameter = ParameterCompleteSerializer(read_only=True) + analyzer_config = rfs.SlugRelatedField(read_only=True, slug_field="name") + connector_config = rfs.SlugRelatedField(read_only=True, slug_field="name") + visualizer_config = rfs.SlugRelatedField(read_only=True, slug_field="name") + ingestor_config = rfs.SlugRelatedField(read_only=True, slug_field="name") + pivot_config = rfs.SlugRelatedField(read_only=True, slug_field="name") + + class Meta: + model = PluginConfig + exclude = ["id"] + + +class AbstractConfigSerializer(rfs.ModelSerializer): + ... + + +class PythonConfigSerializer(AbstractConfigSerializer): + parameters = ParameterSerializer(write_only=True, many=True) + + class Meta: + exclude = [ + "python_module", + "routing_key", + "soft_time_limit", + "health_check_status", + "health_check_task", + ] + list_serializer_class = PythonConfigListSerializer + + def to_internal_value(self, data): + raise NotImplementedError() + + def to_representation(self, instance: PythonConfig): + result = super().to_representation(instance) + result["disabled"] = result["disabled"] | instance.health_check_status + result["config"] = { + "queue": instance.get_routing_key(), + "soft_time_limit": instance.soft_time_limit, + } + return result + + +class AbstractConfigSerializerForMigration(AbstractConfigSerializer): + class Meta: + exclude = ["id"] + + +class PythonConfigSerializerForMigration(AbstractConfigSerializerForMigration): + python_module = PythonModulSerializerComplete(read_only=True) + parameters = ParameterSerializer(write_only=True, many=True) + + class Meta: + exclude = AbstractConfigSerializerForMigration.Meta.exclude + [ + "health_check_task" + ] + + def to_representation(self, instance): + return super().to_representation(instance) diff --git a/Submodules/IntelOwl/api_app/serializers/report.py b/Submodules/IntelOwl/api_app/serializers/report.py new file mode 100644 index 0000000..bd8ec88 --- /dev/null +++ b/Submodules/IntelOwl/api_app/serializers/report.py @@ -0,0 +1,51 @@ +from rest_framework import serializers as rfs + +from api_app.models import AbstractReport +from api_app.serializers import AbstractBIInterface + + +class AbstractReportSerializerInterface(rfs.ModelSerializer): + name = rfs.SlugRelatedField(read_only=True, source="config", slug_field="name") + type = rfs.SerializerMethodField(read_only=True, method_name="get_type") + + class Meta: + fields = ["name", "process_time", "status", "end_time", "parameters", "type"] + list_serializer_class = rfs.ListSerializer + + def get_type(self, instance: AbstractReport): + return instance.__class__.__name__.replace("Report", "").lower() + + def to_internal_value(self, data): + raise NotImplementedError() + + +class AbstractReportBISerializer(AbstractBIInterface): + timestamp = rfs.DateTimeField(source="start_time") + username = rfs.CharField(source="job.user.username") + name = rfs.SlugRelatedField(read_only=True, source="config", slug_field="name") + + class Meta: + fields = AbstractBIInterface.Meta.fields + [ + "parameters", + ] + list_serializer_class = rfs.ListSerializer + + def to_representation(self, instance: AbstractReport): + data = super().to_representation(instance) + return self.to_elastic_dict(data) + + def get_class_instance(self, instance: AbstractReport): + return super().get_class_instance(instance).split("report")[0] + + +class AbstractReportSerializer(AbstractReportSerializerInterface): + class Meta: + fields = AbstractReportSerializerInterface.Meta.fields + [ + "id", + "report", + "errors", + "start_time", + ] + list_serializer_class = ( + AbstractReportSerializerInterface.Meta.list_serializer_class + ) diff --git a/Submodules/IntelOwl/api_app/signals.py b/Submodules/IntelOwl/api_app/signals.py new file mode 100644 index 0000000..6b5a5b2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/signals.py @@ -0,0 +1,146 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import logging +from typing import Type + +from django import dispatch +from django.conf import settings +from django.db import models +from django.dispatch import receiver + +from api_app.decorators import prevent_signal_recursion +from api_app.helpers import calculate_md5 +from api_app.models import ( + Job, + ListCachable, + Parameter, + PluginConfig, + PythonConfig, + PythonModule, +) + +migrate_finished = dispatch.Signal() + +logger = logging.getLogger(__name__) + + +@receiver(models.signals.pre_save, sender=Job) +def pre_save_job(sender, instance: Job, **kwargs): + if not instance.md5: + instance.md5 = calculate_md5( + instance.file.read() + if instance.is_sample + else instance.observable_name.encode("utf-8") + ) + + +@receiver(models.signals.post_save, sender=Job) +@prevent_signal_recursion +def post_save_job(sender, instance: Job, *args, **kwargs): + if instance.finished_analysis_time and instance.received_request_time: + td = instance.finished_analysis_time - instance.received_request_time + instance.process_time = round(td.total_seconds(), 2) + + +@receiver(models.signals.pre_delete, sender=Job) +def pre_delete_job(sender, instance: Job, **kwargs): + if instance.file: + instance.file.delete() + + +@receiver(models.signals.post_delete, sender=Job) +def post_delete_job(sender, instance: Job, **kwargs): + if instance.investigation and instance.investigation.jobs.count() == 0: + instance.investigation.delete() + + +@receiver(migrate_finished) +def post_migrate_api_app( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from django_celery_beat.models import PeriodicTask + + from intel_owl.tasks import update + + for module in PythonModule.objects.filter(health_check_schedule__isnull=False): + for config in module.configs.filter(health_check_task__isnull=True): + config.generate_health_check_periodic_task() + for module in PythonModule.objects.filter( + update_schedule__isnull=False, update_task__isnull=True + ): + module.generate_update_periodic_task() + + for task in PeriodicTask.objects.filter( + enabled=True, task=f"{update.__module__}.{update.__name__}" + ): + task.enabled &= settings.REPO_DOWNLOADER_ENABLED + task.save() + + +@receiver(models.signals.post_save, sender=PluginConfig) +def post_save_plugin_config(sender, instance: PluginConfig, *args, **kwargs): + instance.refresh_cache_keys() + + +@receiver(models.signals.post_delete, sender=PluginConfig) +def post_delete_plugin_config(sender, instance: PluginConfig, *args, **kwargs): + instance.refresh_cache_keys() + + +@receiver(models.signals.post_save, sender=Parameter) +def post_save_parameter(sender, instance: Parameter, *args, **kwargs): + # delete list view cache + instance.refresh_cache_keys() + + +@receiver(models.signals.post_delete, sender=Parameter) +def post_delete_parameter(sender, instance: Parameter, *args, **kwargs): + # delete list view cache + instance.refresh_cache_keys() + + +@receiver(models.signals.post_save, sender=PythonModule) +def post_save_python_module_periodic_tasks( + sender: Type[PythonModule], instance: PythonModule, *args, **kwargs +): + instance.generate_update_periodic_task() + for config in instance.configs.all(): + config.generate_health_check_periodic_task() + + +@receiver(models.signals.post_delete, sender=PythonModule) +def post_delete_python_module_periodic_tasks( + sender: Type[PythonModule], instance: PythonModule, using, origin, *args, **kwargs +): + if hasattr(instance, "update_task") and instance.update_task: + instance.update_task.delete() + + +@receiver(models.signals.post_delete) +def post_delete_python_config_periodic_tasks( + sender: Type[PythonConfig], instance: PythonConfig, using, origin, *args, **kwargs +): + if ( + issubclass(sender, PythonConfig) + and hasattr(instance, "health_check_task") + and instance.health_check_task + ): + instance.health_check_task.delete() + + +@receiver(models.signals.post_save) +def post_save_python_config_cache(sender, instance, *args, **kwargs): + if issubclass(sender, ListCachable): + instance.delete_class_cache_keys() + + +@receiver(models.signals.post_delete) +def post_delete_python_config_cache(sender, instance, using, origin, *args, **kwargs): + if issubclass(sender, ListCachable): + instance.delete_class_cache_keys() diff --git a/Submodules/IntelOwl/api_app/tabulars.py b/Submodules/IntelOwl/api_app/tabulars.py new file mode 100644 index 0000000..3d4ed98 --- /dev/null +++ b/Submodules/IntelOwl/api_app/tabulars.py @@ -0,0 +1,121 @@ +from typing import Optional + +from django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from api_app.forms import ParameterInlineForm +from api_app.models import ( + OrganizationPluginConfiguration, + Parameter, + PluginConfig, + PythonConfig, +) + + +class PluginConfigInlineForParameter(admin.TabularInline): + model = PluginConfig + extra = 0 + fields = ["value"] + + +class PluginConfigInlineForPythonConfig(admin.TabularInline): + model = PluginConfig + extra = 0 + fields = ["parameter", "is_secret", "get_type", "value"] + readonly_fields = ["is_secret", "get_type"] + verbose_name = "Default Plugin Config" + verbose_name_plural = "Default Plugin Configs" + + @admin.display(description="Type") + def get_type(self, instance: PluginConfig): + return instance.parameter.type + + @staticmethod + def has_delete_permission(request, obj=None): + return False + + @staticmethod + def get_parent_pk(request) -> Optional[str]: + parent_pk = request.resolver_match.kwargs.get("object_id") + if parent_pk: + # django encode the url this way when it finds a `_` + parent_pk = parent_pk.replace("_5F", "_") + return parent_pk + + def get_parent(self, request) -> Optional[PythonConfig]: + parent_pk = self.get_parent_pk(request) + if parent_pk: + plugin_config = self.parent_model.objects.get(pk=parent_pk) + return plugin_config + + def formfield_for_foreignkey(self, db_field, request=None, **kwargs): + field = super().formfield_for_foreignkey(db_field, request, **kwargs) + + if db_field.name == "parameter": + parent_model = self.get_parent(request) + # we are creating an object + if parent_model is None: + return field + # the user should add the default for parameters, not secrets + field.queryset = field.queryset.filter( + python_module=parent_model.python_module, is_secret=False + ) + return field + + def get_extra(self, request, obj: PluginConfig = None, **kwargs): + if self.get_parent(request): + return ( + Parameter.objects.filter( + python_module=self.get_parent(request).python_module, + is_secret=False, + ).count() + - PluginConfig.objects.filter( + **{ + self.get_parent( + request + ).snake_case_name.lower(): self.get_parent_pk(request) + }, + owner=None, + for_organization=False, + parameter__is_secret=False + ).count() + ) + return 0 + + def get_queryset(self, request): + return ( + super() + .get_queryset(request) + .filter(owner=None, for_organization=False, parameter__is_secret=False) + ) + + @admin.display(description="Is a secret", boolean=True) + def is_secret(self, instance: PluginConfig): + return instance.parameter.is_secret + + +class ParameterInline(admin.TabularInline): + model = Parameter + list_display = ParameterInlineForm.Meta.fields + fields = list_display + show_change_link = True + + @staticmethod + def get_extra(request, obj: Parameter = None, **kwargs): + return 0 + + +class OrganizationPluginConfigurationInLine(GenericTabularInline): + model = OrganizationPluginConfiguration + list_display = [ + "organization", + "rate_limit_timeout", + "disabled", + "disabled_comment", + ] + fields = list_display + show_change_link = True + + @staticmethod + def get_extra(request, obj: OrganizationPluginConfiguration = None, **kwargs): + return 0 diff --git a/Submodules/IntelOwl/api_app/urls.py b/Submodules/IntelOwl/api_app/urls.py new file mode 100644 index 0000000..b922fa0 --- /dev/null +++ b/Submodules/IntelOwl/api_app/urls.py @@ -0,0 +1,64 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +from .views import ( + CommentViewSet, + JobViewSet, + PluginConfigViewSet, + TagViewSet, + analyze_file, + analyze_multiple_files, + analyze_multiple_observables, + analyze_observable, + ask_analysis_availability, + ask_multi_analysis_availability, + plugin_state_viewer, +) + +# Routers provide an easy way of automatically determining the URL conf. +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"tags", TagViewSet, basename="tags") +router.register(r"jobs", JobViewSet, basename="jobs") +router.register(r"comments", CommentViewSet, basename="comments") +router.register(r"plugin-config", PluginConfigViewSet, basename="plugin-config") + +# These come after /api/.. +urlpatterns = [ + # standalone endpoints + path("ask_analysis_availability", ask_analysis_availability), + path("ask_multi_analysis_availability", ask_multi_analysis_availability), + path("analyze_file", analyze_file), + path( + "analyze_multiple_files", analyze_multiple_files, name="analyze_multiple_files" + ), + path("analyze_observable", analyze_observable), + path( + "analyze_multiple_observables", + analyze_multiple_observables, + name="analyze_multiple_observables", + ), + # router viewsets + path("", include(router.urls)), + # Plugins + path("", include("api_app.analyzers_manager.urls")), + path("", include("api_app.connectors_manager.urls")), + path("", include("api_app.visualizers_manager.urls")), + path("", include("api_app.ingestors_manager.urls")), + path("", include("api_app.pivots_manager.urls")), + path("", include("api_app.playbooks_manager.urls")), + path("", include("api_app.investigations_manager.urls")), + # auth + path("auth/", include("authentication.urls")), + # certego_saas: + # default apps (user), + path("", include("certego_saas.urls")), + # notifications sub-app + path("", include("certego_saas.apps.notifications.urls")), + # organization sub-app + path("me/", include("certego_saas.apps.organization.urls")), + # this is for retro-compatibility + path("plugin-disable/", plugin_state_viewer, name="plugin_state_viewer"), +] diff --git a/Submodules/IntelOwl/api_app/validators.py b/Submodules/IntelOwl/api_app/validators.py new file mode 100644 index 0000000..7eb79f8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/validators.py @@ -0,0 +1,100 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import jsonschema +from django.core.exceptions import ValidationError +from django.core.validators import RegexValidator + +from api_app.choices import ParamTypes + +plugin_name_validator = RegexValidator( + r"^\w+$", "Your name should match the [A-Za-z0-9_] characters" +) + + +def validate_schema(value, schema): + try: + return jsonschema.validate(value, schema=schema) + except jsonschema.exceptions.ValidationError as e: + raise ValidationError(e.message) + + +def validate_secrets(value): + schema = { + "type": "object", + "title": "Secret", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": { + "type": "object", + "properties": { + "description": {"type": "string"}, + "required": {"type": "boolean"}, + "type": {"enum": ParamTypes.values}, + "default": { + "type": ["string", "boolean", "array", "number", "object"] + }, + }, + "additionalProperties": False, + "required": ["description", "required", "type"], + }, + }, + "additionalProperties": False, + } + return validate_schema(value, schema) + + +def validate_params(value): + schema = { + "type": "object", + "title": "Param", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": { + "type": "object", + "properties": { + "type": {"enum": ParamTypes.values}, + "description": {"type": "string"}, + "default": {}, + }, + "additionalProperties": False, + "required": ["type", "description", "default"], + }, + }, + "additionalProperties": False, + } + return validate_schema(value, schema) + + +def validate_runtime_configuration(value): + schema = { + "type": "object", + "title": "RuntimeConfig", + "properties": { + "analyzers": { + "type": "object", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": {"type": "object"}, + }, + }, + "connectors": { + "type": "object", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": {"type": "object"}, + }, + }, + "pivots": { + "type": "object", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": {"type": "object"}, + }, + }, + "visualizers": { + "type": "object", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": {"type": "object"}, + }, + }, + }, + "additionalProperties": False, + "required": ["analyzers", "connectors", "visualizers"], + } + return validate_schema(value, schema) diff --git a/Submodules/IntelOwl/api_app/views.py b/Submodules/IntelOwl/api_app/views.py new file mode 100644 index 0000000..25408ed --- /dev/null +++ b/Submodules/IntelOwl/api_app/views.py @@ -0,0 +1,930 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import datetime +import logging +import uuid +from abc import ABCMeta, abstractmethod + +from django.db.models import Count, Q +from django.db.models.functions import Trunc +from django.http import FileResponse +from django.utils.timezone import now +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import extend_schema as add_docs +from drf_spectacular.utils import inline_serializer +from rest_framework import serializers as rfs +from rest_framework import status, viewsets +from rest_framework.decorators import action, api_view +from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework.viewsets import ModelViewSet + +from api_app.websocket import JobConsumer +from certego_saas.apps.organization.permissions import ( + IsObjectOwnerOrSameOrgPermission as IsObjectUserOrSameOrgPermission, +) +from certego_saas.apps.organization.permissions import ( + IsObjectOwnerPermission as IsObjectUserPermission, +) +from certego_saas.ext.helpers import cache_action_response, parse_humanized_range +from certego_saas.ext.mixins import SerializerActionMixin +from certego_saas.ext.viewsets import ReadAndDeleteOnlyViewSet +from intel_owl import tasks +from intel_owl.celery import app as celery_app + +from .analyzers_manager.constants import ObservableTypes +from .choices import ObservableClassification +from .decorators import deprecated_endpoint +from .filters import JobFilter +from .mixins import PaginationMixin +from .models import ( + AbstractConfig, + AbstractReport, + Comment, + Job, + OrganizationPluginConfiguration, + PluginConfig, + PythonConfig, + Tag, +) +from .permissions import IsObjectAdminPermission, IsObjectOwnerPermission +from .pivots_manager.models import PivotConfig +from .serializers.job import ( + CommentSerializer, + FileJobSerializer, + JobAvailabilitySerializer, + JobListSerializer, + JobRecentScanSerializer, + JobResponseSerializer, + ObservableAnalysisSerializer, + RestJobSerializer, + TagSerializer, +) +from .serializers.plugin import PluginConfigSerializer, PythonConfigSerializer + +logger = logging.getLogger(__name__) + + +# REST API endpoints + + +@add_docs( + description=""" + This is useful to avoid repeating the same analysis multiple times. + By default this API checks if there are existing analysis related to the md5 in + status "running" or "reported_without_fails" + Also, you need to specify the analyzers needed because, otherwise, it is + highly probable that you won't get all the results that you expect""", + request=JobAvailabilitySerializer, + responses={ + 200: inline_serializer( + name="AskAnalysisAvailabilitySuccessResponse", + fields={ + "status": rfs.StringRelatedField(), + "job_id": rfs.StringRelatedField(), + "analyzers_to_execute": OpenApiTypes.OBJECT, + }, + ), + }, +) +@deprecated_endpoint(deprecation_date="01-07-2023") +@api_view(["POST"]) +def ask_analysis_availability(request): + serializer = JobAvailabilitySerializer( + data=request.data, context={"request": request} + ) + serializer.is_valid(raise_exception=True) + try: + job = serializer.save() + except Job.DoesNotExist: + result = None + else: + result = job + return Response( + JobResponseSerializer(result).data, + status=status.HTTP_200_OK, + ) + + +@add_docs( + description=""" + This is useful to avoid repeating the same analysis multiple times. + By default this API checks if there are existing analysis related to the md5 in + status "running" or "reported_without_fails" + Also, you need to specify the analyzers needed because, otherwise, it is + highly probable that you won't get all the results that you expect. + NOTE: This API is similar to ask_analysis_availability, but it allows multiple + md5s to be checked at the same time.""", + responses={200: JobAvailabilitySerializer(many=True)}, +) +@api_view(["POST"]) +def ask_multi_analysis_availability(request): + logger.info(f"received ask_multi_analysis_availability from user {request.user}") + serializer = JobAvailabilitySerializer( + data=request.data, context={"request": request}, many=True + ) + serializer.is_valid(raise_exception=True) + try: + jobs = serializer.save() + except Job.DoesNotExist: + result = [] + else: + result = jobs + jrs = JobResponseSerializer(result, many=True).data + logger.info(f"finished ask_multi_analysis_availability from user {request.user}") + return Response( + jrs, + status=status.HTTP_200_OK, + ) + + +@add_docs( + description="This endpoint allows to start a Job related for a single File." + " Retained for retro-compatibility", + request=FileJobSerializer, + responses={200: JobResponseSerializer(many=True)}, +) +@api_view(["POST"]) +def analyze_file(request): + logger.info(f"received analyze_file from user {request.user}") + fas = FileJobSerializer(data=request.data, context={"request": request}) + fas.is_valid(raise_exception=True) + job = fas.save(send_task=True) + jrs = JobResponseSerializer(job).data + logger.info(f"finished analyze_file from user {request.user}") + return Response( + jrs, + status=status.HTTP_200_OK, + ) + + +@add_docs( + description="This endpoint allows to start Jobs related to multiple Files", + # It should be better to link the doc to the related MultipleFileAnalysisSerializer. + # It is not straightforward because you can't just add a class + # which extends a ListSerializer. + # Follow this doc to try to find a fix: + # https://drf-spectacular.readthedocs.io/en/latest/customization.html#declare-serializer-magic-with + # -openapiserializerextension + request=inline_serializer( + name="MultipleFilesSerializer", + fields={ + "files": rfs.ListField(child=rfs.FileField()), + "file_names": rfs.ListField(child=rfs.CharField()), + "file_mimetypes": rfs.ListField(child=rfs.CharField()), + }, + ), + responses={200: JobResponseSerializer}, +) +@api_view(["POST"]) +def analyze_multiple_files(request): + logger.info(f"received analyze_multiple_files from user {request.user}") + fas = FileJobSerializer(data=request.data, context={"request": request}, many=True) + fas.is_valid(raise_exception=True) + parent_job = fas.validated_data[0].get("parent_job", None) + jobs = fas.save(send_task=True, parent=parent_job) + jrs = JobResponseSerializer(jobs, many=True).data + logger.info(f"finished analyze_multiple_files from user {request.user}") + return Response( + jrs, + status=status.HTTP_200_OK, + ) + + +@add_docs( + description="This endpoint allows to start a Job related to an observable. " + "Retained for retro-compatibility", + request=ObservableAnalysisSerializer, + responses={200: JobResponseSerializer}, +) +@api_view(["POST"]) +def analyze_observable(request): + logger.info(f"received analyze_observable from user {request.user}") + oas = ObservableAnalysisSerializer(data=request.data, context={"request": request}) + oas.is_valid(raise_exception=True) + job = oas.save(send_task=True) + jrs = JobResponseSerializer(job).data + logger.info(f"finished analyze_observable from user {request.user}") + return Response( + jrs, + status=status.HTTP_200_OK, + ) + + +@add_docs( + description="""This endpoint allows to start Jobs related to multiple observables. + Observable parameter must be composed like this: + [(, ), ...]""", + request=inline_serializer( + name="MultipleObservableSerializer", + fields={ + "observables": rfs.ListField( + child=rfs.ListField(max_length=2, min_length=2) + ) + }, + ), + responses={200: JobResponseSerializer}, +) +@api_view(["POST"]) +def analyze_multiple_observables(request): + logger.info(f"received analyze_multiple_observables from user {request.user}") + oas = ObservableAnalysisSerializer( + data=request.data, many=True, context={"request": request} + ) + oas.is_valid(raise_exception=True) + parent_job = oas.validated_data[0].get("parent_job", None) + jobs = oas.save(send_task=True, parent=parent_job) + jrs = JobResponseSerializer(jobs, many=True).data + logger.info(f"finished analyze_multiple_observables from user {request.user}") + return Response( + jrs, + status=status.HTTP_200_OK, + ) + + +@add_docs( + description=""" + REST endpoint to fetch list of job comments or + retrieve/delete a job comment with job comment ID. + Requires authentication. + """ +) +class CommentViewSet(ModelViewSet): + queryset = Comment.objects.all() + serializer_class = CommentSerializer + permission_classes = [IsAuthenticated] + + def get_permissions(self): + permissions = super().get_permissions() + + # only the owner of the comment can update or delete the comment + if self.action in ["destroy", "update", "partial_update"]: + permissions.append(IsObjectUserPermission()) + # the owner and anyone in the org can read the comment + if self.action in ["retrieve"]: + permissions.append(IsObjectUserOrSameOrgPermission()) + + return permissions + + def get_queryset(self): + queryset = super().get_queryset() + jobs = Job.objects.visible_for_user(self.request.user).values_list( + "pk", flat=True + ) + return queryset.filter(job__id__in=jobs) + + +@add_docs( + description=""" + REST endpoint to fetch list of jobs or retrieve/delete a job with job ID. + Requires authentication. + """ +) +class JobViewSet(ReadAndDeleteOnlyViewSet, SerializerActionMixin): + queryset = ( + Job.objects.prefetch_related("tags").order_by("-received_request_time").all() + ) + serializer_class = RestJobSerializer + serializer_action_classes = { + "retrieve": RestJobSerializer, + "list": JobListSerializer, + } + filterset_class = JobFilter + ordering_fields = [ + "received_request_time", + "finished_analysis_time", + "process_time", + ] + + def get_permissions(self): + permissions = super().get_permissions() + if self.action in ["destroy", "kill"]: + permissions.append(IsObjectUserOrSameOrgPermission()) + return permissions + + def get_queryset(self): + user = self.request.user + logger.info( + f"user: {user} request the jobs with params: {self.request.query_params}" + ) + return Job.objects.visible_for_user(user).order_by("-received_request_time") + + @action(detail=False, methods=["post"]) + def recent_scans(self, request): + if "md5" not in request.data: + raise ValidationError({"detail": "md5 is required"}) + max_temporal_distance = request.data.get("max_temporal_distance", 14) + jobs = ( + Job.objects.filter(md5=request.data["md5"]) + .visible_for_user(self.request.user) + .filter( + finished_analysis_time__gte=now() + - datetime.timedelta(days=max_temporal_distance) + ) + .annotate_importance(request.user) + .order_by("-importance", "-finished_analysis_time") + ) + return Response( + JobRecentScanSerializer(jobs, many=True).data, status=status.HTTP_200_OK + ) + + @action(detail=False, methods=["post"]) + def recent_scans_user(self, request): + limit = request.data.get("limit", 5) + if "is_sample" not in request.data: + raise ValidationError({"detail": "is_sample is required"}) + jobs = ( + Job.objects.filter(user__pk=request.user.pk) + .filter(is_sample=request.data["is_sample"]) + .annotate_importance(request.user) + .order_by("-importance", "-finished_analysis_time")[:limit] + ) + return Response( + JobRecentScanSerializer(jobs, many=True).data, status=status.HTTP_200_OK + ) + + @action(detail=True, methods=["patch"]) + def retry(self, request, pk=None): + job = self.get_object() + if job.status not in Job.Status.final_statuses(): + raise ValidationError({"detail": "Job is running"}) + job.retry() + return Response(status=status.HTTP_204_NO_CONTENT) + + @add_docs( + description="Kill running job by closing celery tasks and marking as killed", + request=None, + responses={ + 204: None, + }, + ) + @action(detail=True, methods=["patch"]) + def kill(self, request, pk=None): + # get job object or raise 404 + job = self.get_object() + + # check if job running + if job.status in Job.Status.final_statuses(): + raise ValidationError({"detail": "Job is not running"}) + # close celery tasks and mark reports as killed + job.kill_if_ongoing() + return Response(status=status.HTTP_204_NO_CONTENT) + + @add_docs( + description="Download file/sample associated with a job", + request=None, + responses={200: OpenApiTypes.BINARY, 400: None}, + ) + @action(detail=True, methods=["get"]) + def download_sample(self, request, pk=None): + """ + Download a sample from a given Job ID. + + :param url: pk (job_id) + :returns: bytes + """ + # get job object + job = self.get_object() + + # make sure it is a sample + if not job.is_sample: + raise ValidationError( + {"detail": "Requested job does not have a sample associated with it."} + ) + return FileResponse( + job.file, + filename=job.file_name, + content_type=job.file_mimetype, + as_attachment=True, + ) + + @add_docs(description="Pivot a job") + @action( + detail=True, methods=["post"] + ) # , url_path="pivot-(?P\d+)") + def pivot(self, request, pk=None, pivot_config_pk=None): + starting_job = self.get_object() + try: + pivot_config: PivotConfig = PivotConfig.objects.get(pk=pivot_config_pk) + except PivotConfig.DoesNotExist: + raise ValidationError({"detail": "Requested pivot config does not exist."}) + else: + try: + pivots = pivot_config.pivot_job(starting_job.reports) + except KeyError: + msg = ( + f"Unable to retrieve value at {self.field}" + f" from job {starting_job.pk}" + ) + logger.error(msg) + raise ValidationError({"detail": msg}) + except Exception as e: + logger.exception(e) + raise ValidationError( + {"detail": f"Unable to start pivot from job {starting_job.pk}"} + ) + else: + return Response( + [pivot.ending_job.pk for pivot in pivots], + status=status.HTTP_201_CREATED, + ) + + @action( + url_path="aggregate/status", + detail=False, + methods=["GET"], + ) + @cache_action_response(timeout=60 * 5) + def aggregate_status(self, request): + annotations = { + key.lower(): Count("status", filter=Q(status=key)) + for key in Job.Status.values + } + return self.__aggregation_response_static( + annotations, users=self.get_org_members(request) + ) + + @action( + url_path="aggregate/type", + detail=False, + methods=["GET"], + ) + @cache_action_response(timeout=60 * 5) + def aggregate_type(self, request): + annotations = { + "file": Count("is_sample", filter=Q(is_sample=True)), + "observable": Count("is_sample", filter=Q(is_sample=False)), + } + return self.__aggregation_response_static( + annotations, users=self.get_org_members(request) + ) + + @action( + url_path="aggregate/observable_classification", + detail=False, + methods=["GET"], + ) + @cache_action_response(timeout=60 * 5) + def aggregate_observable_classification(self, request): + annotations = { + oc.lower(): Count( + "observable_classification", filter=Q(observable_classification=oc) + ) + for oc in ObservableTypes.values + } + return self.__aggregation_response_static( + annotations, users=self.get_org_members(request) + ) + + @action( + url_path="aggregate/file_mimetype", + detail=False, + methods=["GET"], + ) + @cache_action_response(timeout=60 * 5) + def aggregate_file_mimetype(self, request): + return self.__aggregation_response_dynamic( + "file_mimetype", users=self.get_org_members(request) + ) + + @action( + url_path="aggregate/observable_name", + detail=False, + methods=["GET"], + ) + @cache_action_response(timeout=60 * 5) + def aggregate_observable_name(self, request): + return self.__aggregation_response_dynamic( + "observable_name", False, users=self.get_org_members(request) + ) + + @action( + url_path="aggregate/md5", + detail=False, + methods=["GET"], + ) + @cache_action_response(timeout=60 * 5) + def aggregate_md5(self, request): + # this is for file + return self.__aggregation_response_dynamic( + "md5", False, users=self.get_org_members(request) + ) + + @staticmethod + def get_org_members(request): + user = request.user + org_param = request.GET.get("org", "").lower() == "true" + users_of_organization = None + if org_param: + organization = user.membership.organization + users_of_organization = [ + membership.user for membership in organization.members.all() + ] + return users_of_organization + + def __aggregation_response_static(self, annotations: dict, users=None) -> Response: + delta, basis = self.__parse_range(self.request) + filter_kwargs = {"received_request_time__gte": delta} + if users: + filter_kwargs["user__in"] = users + qs = ( + Job.objects.filter(**filter_kwargs) + .annotate(date=Trunc("received_request_time", basis)) + .values("date") + .annotate(**annotations) + ) + return Response(qs) + + def __aggregation_response_dynamic( + self, + field_name: str, + group_by_date: bool = True, + limit: int = 5, + users=None, + ) -> Response: + delta, basis = self.__parse_range(self.request) + filter_kwargs = {"received_request_time__gte": delta} + if users: + filter_kwargs["user__in"] = users + if field_name == "md5": + filter_kwargs["is_sample"] = True + + most_frequent_values = ( + Job.objects.filter(**filter_kwargs) + .exclude(**{f"{field_name}__isnull": True}) + .exclude(**{f"{field_name}__exact": ""}) + # excluding those because they could lead to SQL query errors + .exclude( + observable_classification__in=[ + ObservableClassification.URL, + ObservableClassification.GENERIC, + ] + ) + .annotate(count=Count(field_name)) + .distinct() + .order_by("-count")[:limit] + .values_list(field_name, flat=True) + ) + + logger.info( + f"request: {field_name} found most_frequent_values: {most_frequent_values}" + ) + + if len(most_frequent_values): + annotations = { + val: Count(field_name, filter=Q(**{field_name: val})) + for val in most_frequent_values + } + logger.debug(f"request: {field_name} annotations: {annotations}") + if group_by_date: + aggregation = ( + Job.objects.filter(**filter_kwargs) + .annotate(date=Trunc("received_request_time", basis)) + .values("date") + .annotate(**annotations) + ) + else: + aggregation = Job.objects.filter(**filter_kwargs).aggregate( + **annotations + ) + else: + aggregation = {} + + return Response( + { + "values": most_frequent_values, + "aggregation": aggregation, + } + ) + + @staticmethod + def __parse_range(request): + try: + range_str = request.GET["range"] + except KeyError: + # default + range_str = "7d" + + return parse_humanized_range(range_str) + + +@add_docs( + description=""" + REST endpoint to perform CRUD operations on ``Tag`` model. + Requires authentication. + """ +) +class TagViewSet(viewsets.ModelViewSet): + queryset = Tag.objects.all() + serializer_class = TagSerializer + pagination_class = None + + +class ModelWithOwnershipViewSet(viewsets.ModelViewSet): + def get_queryset(self): + qs = super().get_queryset() + if self.action == "list": + return qs.visible_for_user(self.request.user) + return qs + + def get_permissions(self): + permissions = super().get_permissions() + if self.action in ["destroy", "update"]: + if self.request.method == "PUT": + raise PermissionDenied() + # code quality checker marks this as error, but it works correctly + permissions.append( + ( # skipcq: PYL-E1102 + IsObjectAdminPermission | IsObjectOwnerPermission + )() + ) + + return permissions + + +@add_docs( + description=""" + REST endpoint to fetch list of PluginConfig or retrieve/delete a CustomConfig. + Requires authentication. Allows access to only authorized CustomConfigs. + """ +) +class PluginConfigViewSet(ModelWithOwnershipViewSet): + serializer_class = PluginConfigSerializer + pagination_class = None + queryset = PluginConfig.objects.all() + + def get_queryset(self): + # the .exclude is to remove the default values + return super().get_queryset().exclude(owner__isnull=True).order_by("id") + + +@add_docs( + description="""This endpoint allows organization owners + and members to view plugin state.""", + responses={ + 200: inline_serializer( + name="PluginStateViewerResponseSerializer", + fields={ + "data": rfs.JSONField(), + }, + ), + }, +) +@api_view(["GET"]) +def plugin_state_viewer(request): + if not request.user.has_membership(): + raise PermissionDenied() + + result = {"data": {}} + for opc in OrganizationPluginConfiguration.objects.filter(disabled=True): + result["data"][opc.config.name] = { + "disabled": True, + } + return Response(result) + + +class PythonReportActionViewSet(viewsets.GenericViewSet, metaclass=ABCMeta): + permission_classes = [ + IsObjectUserOrSameOrgPermission, + ] + + @classmethod + @property + @abstractmethod + def report_model(cls): + raise NotImplementedError() + + def get_queryset(self): + return self.report_model.objects.all() + + def get_object(self, job_id: int, report_id: int) -> AbstractReport: + """ + overrides drf's get_object + get plugin report object by name and job_id + """ + try: + obj = self.report_model.objects.get( + job_id=job_id, + pk=report_id, + ) + except self.report_model.DoesNotExist: + raise NotFound() + else: + self.check_object_permissions(self.request, obj) + return obj + + @staticmethod + def perform_kill(report: AbstractReport): + """ + performs kill + override for callbacks after kill operation + """ + # kill celery task + celery_app.control.revoke(report.task_id, terminate=True) + # update report + report.status = AbstractReport.Status.KILLED + report.save(update_fields=["status"]) + # clean up job + + job = Job.objects.get(pk=report.job.pk) + job.set_final_status() + JobConsumer.serialize_and_send_job(job) + + @staticmethod + def perform_retry(report: AbstractReport): + report.errors.clear() + report.save(update_fields=["errors"]) + try: + signature = next( + report.config.__class__.objects.filter(pk=report.config.pk) + .annotate_runnable(report.job.user) + .get_signatures( + report.job, + ) + ) + except StopIteration: + raise RuntimeError(f"Unable to find signature for report {report.pk}") + runner = signature | tasks.job_set_final_status.signature( + args=[report.job.id], + kwargs={}, + queue=report.config.queue, + immutable=True, + MessageGroupId=str(uuid.uuid4()), + priority=report.job.priority, + ) + runner() + + @add_docs( + description="Kill running plugin by closing celery task and marking as killed", + request=None, + responses={ + 204: None, + }, + ) + @action(detail=False, methods=["patch"]) + def kill(self, request, job_id, report_id): + logger.info( + f"kill request from user {request.user}" + f" for job_id {job_id}, pk {report_id}" + ) + # get report object or raise 404 + report = self.get_object(job_id, report_id) + if report.status not in [ + AbstractReport.Status.RUNNING, + AbstractReport.Status.PENDING, + ]: + raise ValidationError({"detail": "Plugin is not running or pending"}) + + self.perform_kill(report) + return Response(status=status.HTTP_204_NO_CONTENT) + + @add_docs( + description="Retry a plugin run if it failed/was killed previously", + request=None, + responses={ + 204: None, + }, + ) + @action(detail=False, methods=["patch"]) + def retry(self, request, job_id, report_id): + logger.info( + f"retry request from user {request.user}" + f" for job_id {job_id}, report_id {report_id}" + ) + # get report object or raise 404 + report = self.get_object(job_id, report_id) + if report.status not in [ + AbstractReport.Status.FAILED, + AbstractReport.Status.KILLED, + ]: + raise ValidationError( + {"detail": "Plugin status should be failed or killed"} + ) + + # retry with the same arguments + try: + self.perform_retry(report) + except StopIteration: + logger.exception(f"Unable to find signature for report {report.pk}") + return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + return Response(status=status.HTTP_204_NO_CONTENT) + + +class AbstractConfigViewSet( + PaginationMixin, viewsets.ReadOnlyModelViewSet, metaclass=ABCMeta +): + permission_classes = [IsAuthenticated] + ordering = ["name"] + lookup_field = "name" + + @add_docs( + description="Disable/Enable plugin for your organization", + request=None, + responses={201: {}, 202: {}}, + ) + @action( + methods=["post"], + detail=True, + url_path="organization", + ) + def disable_in_org(self, request, name=None): + logger.info(f"get disable_in_org from user {request.user}, name {name}") + obj: AbstractConfig = self.get_object() + if request.user.has_membership(): + if not request.user.membership.is_admin: + raise PermissionDenied() + else: + raise PermissionDenied() + organization = request.user.membership.organization + org_configuration = obj.get_or_create_org_configuration(organization) + if org_configuration.disabled: + raise ValidationError({"detail": f"Plugin {obj.name} already disabled"}) + org_configuration.disable_manually(request.user) + return Response(status=status.HTTP_201_CREATED) + + @disable_in_org.mapping.delete + def enable_in_org(self, request, name=None): + logger.info(f"get enable_in_org from user {request.user}, name {name}") + obj: AbstractConfig = self.get_object() + if request.user.has_membership(): + if not request.user.membership.is_admin: + raise PermissionDenied() + else: + raise PermissionDenied() + organization = request.user.membership.organization + org_configuration = obj.get_or_create_org_configuration(organization) + if not org_configuration.disabled: + raise ValidationError({"detail": f"Plugin {obj.name} already enabled"}) + org_configuration.enable_manually(request.user) + return Response(status=status.HTTP_202_ACCEPTED) + + +class PythonConfigViewSet(AbstractConfigViewSet): + serializer_class = PythonConfigSerializer + + def get_queryset(self): + return self.serializer_class.Meta.model.objects.all().prefetch_related( + "python_module__parameters" + ) + + @add_docs( + description="Health Check: " + "if server instance associated with plugin is up or not", + request=None, + responses={ + 200: inline_serializer( + name="PluginHealthCheckSuccessResponse", + fields={ + "status": rfs.BooleanField(allow_null=True), + }, + ), + }, + ) + @action( + methods=["get"], + detail=True, + url_path="health_check", + ) + def health_check(self, request, name=None): + logger.info(f"get healthcheck from user {request.user}, name {name}") + config: PythonConfig = self.get_object() + python_obj = config.python_module.python_class(config) + try: + health_status = python_obj.health_check(request.user) + except NotImplementedError as e: + logger.info(f"NotImplementedError {e}, user {request.user}, name {name}") + raise ValidationError({"detail": "No healthcheck implemented"}) + except Exception as e: + logger.exception(e) + raise ValidationError( + {"detail": "Unexpected exception raised. Check the code."} + ) + else: + return Response(data={"status": health_status}, status=status.HTTP_200_OK) + + @action( + methods=["post"], + detail=True, + url_path="pull", + ) + def pull(self, request, name=None): + logger.info(f"post pull from user {request.user}, name {name}") + obj: PythonConfig = self.get_object() + python_obj = obj.python_module.python_class(obj) + try: + update_status = python_obj.update() + except NotImplementedError as e: + raise ValidationError({"detail": str(e)}) + except Exception as e: + logger.exception(e) + raise ValidationError( + {"detail": "Unexpected exception raised. Check the code."} + ) + else: + if update_status is None: + raise ValidationError( + {"detail": "This Plugin has no Update implemented"} + ) + return Response(data={"status": update_status}, status=status.HTTP_200_OK) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/__init__.py b/Submodules/IntelOwl/api_app/visualizers_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/admin.py b/Submodules/IntelOwl/api_app/visualizers_manager/admin.py new file mode 100644 index 0000000..4f41169 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/admin.py @@ -0,0 +1,21 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.contrib import admin + +from api_app.admin import AbstractReportAdminView, PythonConfigAdminView +from api_app.visualizers_manager.models import VisualizerConfig, VisualizerReport + + +@admin.register(VisualizerReport) +class VisualizerReportAdminView(AbstractReportAdminView): + ... + + +@admin.register(VisualizerConfig) +class VisualizerConfigAdminView(PythonConfigAdminView): + list_display = PythonConfigAdminView.list_display + ("get_playbooks",) + + @admin.display(description="Playbooks") + def get_playbooks(self, instance: VisualizerConfig): + return list(instance.playbooks.values_list("name", flat=True)) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/apps.py b/Submodules/IntelOwl/api_app/visualizers_manager/apps.py new file mode 100644 index 0000000..60640d4 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class VisualizersManagerConfig(AppConfig): + name = "api_app.visualizers_manager" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/choices.py b/Submodules/IntelOwl/api_app/visualizers_manager/choices.py new file mode 100644 index 0000000..51a7965 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/choices.py @@ -0,0 +1,7 @@ +from django.db import models + + +class Position(models.TextChoices): + LEFT = "left" + CENTER = "center" + RIGHT = "right" diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/classes.py b/Submodules/IntelOwl/api_app/visualizers_manager/classes.py new file mode 100644 index 0000000..f86b000 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/classes.py @@ -0,0 +1,460 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +import abc +import logging +from enum import Enum +from typing import Any, Dict, List, Tuple, Type, Union + +from django.db.models import QuerySet + +from api_app.choices import PythonModuleBasePaths +from api_app.classes import Plugin +from api_app.models import AbstractReport +from api_app.visualizers_manager.enums import ( + VisualizableAlignment, + VisualizableColor, + VisualizableIcon, + VisualizableLevelSize, + VisualizableSize, +) +from api_app.visualizers_manager.exceptions import ( + VisualizerConfigurationException, + VisualizerRunException, +) +from api_app.visualizers_manager.models import VisualizerConfig, VisualizerReport + +logger = logging.getLogger(__name__) + + +class VisualizableObject: + def __init__( + self, + size: VisualizableSize = VisualizableSize.S_AUTO, + alignment: VisualizableAlignment = VisualizableAlignment.AROUND, + disable: bool = True, + ): + self.size = size + self.alignment = alignment + self.disable = disable + + @property + def attributes(self) -> List[str]: + return ["size", "alignment", "disable"] + + @property + @abc.abstractmethod + def type(self): + raise NotImplementedError() + + def __bool__(self): + return True + + def to_dict(self) -> Dict: + if not self and not self.disable: + return {} + + result = {attr: getattr(self, attr) for attr in self.attributes} + for key, value in result.items(): + if isinstance(value, VisualizableObject): + result[key] = value.to_dict() + elif isinstance(value, Enum): + result[key] = value.value + + result["type"] = self.type + return result + + +class VisualizableBase(VisualizableObject): + def __init__( + self, + value: Any = "", + size: VisualizableSize = VisualizableSize.S_AUTO, + alignment: VisualizableAlignment = VisualizableAlignment.CENTER, + color: VisualizableColor = VisualizableColor.TRANSPARENT, + link: str = "", + # you can use an element of the enum or an iso3166 alpha2 code (for flags) + icon: Union[VisualizableIcon, str] = VisualizableIcon.EMPTY, + bold: bool = False, + italic: bool = False, + disable: bool = True, + copy_text: str = "", + description: str = "", + ): + super().__init__(size, alignment, disable) + self.value = value + self.color = color + self.link = link + self.icon = icon + self.bold = bold + self.italic = italic + self.copy_text = copy_text or value + self.description = description + + @property + def attributes(self) -> List[str]: + return super().attributes + [ + "value", + "color", + "link", + "icon", + "bold", + "italic", + "copy_text", + "description", + ] + + @property + def type(self) -> str: + return "base" + + def __bool__(self): + return bool(self.value) or bool(self.icon) + + +class VisualizableTitle(VisualizableObject): + def __init__( + self, + title: VisualizableBase, + value: VisualizableObject, + alignment: VisualizableAlignment = VisualizableAlignment.CENTER, + size: VisualizableSize = VisualizableSize.S_AUTO, + disable: bool = True, + ): + super().__init__(size, alignment, disable) + self.title = title + self.value = value + if self.disable != self.title.disable or self.disable != self.value.disable: + logger.warning( + "Each part of the title should be disabled. " + f"Forcing all to disable={self.disable}" + ) + self.title.disable = self.disable + self.value.disable = self.disable + + @property + def attributes(self) -> List[str]: + return super().attributes + ["title", "value"] + + @property + def type(self) -> str: + return "title" + + +class VisualizableBool(VisualizableBase): + def __init__( + self, + value: str, + disable: bool, + *args, + size: VisualizableSize = VisualizableSize.S_AUTO, + color: VisualizableColor = VisualizableColor.DANGER, + **kwargs, + ): + super().__init__( + *args, value=value, size=size, color=color, disable=disable, **kwargs + ) + + def __bool__(self): + return bool(self.value) + + @property + def type(self) -> str: + return "bool" + + def to_dict(self) -> Dict: + result = super().to_dict() + # bool does not support bold because the default is bold + result.pop("bold", None) + # bool does not support alignment: it's a stand alone component + result.pop("alignment", None) + return result + + +class VisualizableListMixin: + def to_dict(self) -> Dict: + result = super().to_dict() # noqa + values: List[VisualizableObject] = result.pop("value", []) + if any(x for x in values): + result["values"] = [val.to_dict() for val in values if val is not None] + else: + result["values"] = [] + return result + + +class VisualizableVerticalList(VisualizableListMixin, VisualizableObject): + def __init__( + self, + value: List[VisualizableObject], + name: VisualizableBase = None, + start_open: bool = False, # noqa + add_count_in_title: bool = True, + fill_empty: bool = True, + alignment: VisualizableAlignment = VisualizableAlignment.CENTER, + size: VisualizableSize = VisualizableSize.S_AUTO, + disable: bool = True, + max_elements_number: int = -1, + report: AbstractReport = None, + ): + super().__init__( + size=size, + alignment=alignment, + disable=disable, + ) + if name and add_count_in_title: + name.value += f" ({len(value)})" + for v in value: + if isinstance(v, str): + raise TypeError( + f"value {v} should be a VisualizableObject and not a string" + ) + if fill_empty and not value: + value = [VisualizableBase(value="no data available", disable=False)] + if not name: + start_open = True + self.value = value + self.name = name + self.add_count_in_title = add_count_in_title + self.start_open = start_open + self.max_elements_number = max_elements_number + self.report = report + + @property + def attributes(self) -> List[str]: + return super().attributes + ["name", "start_open", "value"] + + def __bool__(self): + return True + + @property + def more_elements_object(self) -> VisualizableBase: + link = "" + description = "" + disable = True + if self.report: + link = f"{self.report.job.url}/raw/{self.report.config.plugin_name.lower()}" + description = ( + f"Inspect {self.report.config.name} " + f"{self.report.config.plugin_name.lower()} to view all the results." + ) + disable = False + return VisualizableBase( + value="...", + bold=True, + link=link, + description=description, + disable=disable, + ) + + def to_dict(self) -> Dict: + result = super().to_dict() + if self and self.max_elements_number > 0: + current_elements_number = len(result["values"]) + result["values"] = result["values"][: self.max_elements_number] + # if there are some elements that i'm not displaying + if current_elements_number - self.max_elements_number > 0: + result["values"].append(self.more_elements_object.to_dict()) + + return result + + @property + def type(self) -> str: + return "vertical_list" + + +class VisualizableTable(VisualizableObject): + def __init__( + self, + columns: List[str], + data: List[Dict[str, VisualizableObject]], + size: VisualizableSize = VisualizableSize.S_AUTO, + alignment: VisualizableAlignment = VisualizableAlignment.AROUND, + page_size: int = 5, + disable_filters: bool = False, + disable_sort_by: bool = False, + ): + super().__init__(size=size, alignment=alignment, disable=False) + self.data = data + self.columns = columns + self.page_size = page_size + self.disable_filters = disable_filters + self.disable_sort_by = disable_sort_by + + @property + def attributes(self) -> List[str]: + return super().attributes + [ + "data", + "columns", + "page_size", + "disable_filters", + "disable_sort_by", + ] + + @property + def type(self) -> str: + return "table" + + def to_dict(self) -> Dict: + result = super().to_dict() + data: List[Dict[str, VisualizableObject]] = result.pop("data", []) + if any(x for x in data): + new_data = [] + for element in data: + new_data.append( + { + key: value.to_dict() + for [key, value] in element.items() + if value is not None + } + ) + result["data"] = new_data + else: + result["data"] = [] + result.pop("disable") + return result + + +class VisualizableHorizontalList(VisualizableListMixin, VisualizableObject): + def __init__( + self, + value: List[VisualizableObject], + alignment: VisualizableAlignment = VisualizableAlignment.AROUND, + ): + super().__init__(alignment=alignment, disable=False) + self.value = value + + @property + def attributes(self) -> List[str]: + return super().attributes + ["value"] + + @property + def type(self) -> str: + return "horizontal_list" + + def to_dict(self) -> Dict: + result = super().to_dict() + # currently hlist doesn't support disable and size + result.pop("disable") + result.pop("size") + return result + + +class VisualizableLevel: + def __init__( + self, + position: int, + size: VisualizableLevelSize = VisualizableLevelSize.S_6, + horizontal_list: VisualizableHorizontalList = VisualizableHorizontalList( + value=[] + ), + ): + self._position = position + self._size = size + self._horizontal_list = horizontal_list + + def to_dict(self): + return { + "level_position": self._position, + "level_size": self._size.value, + "elements": self._horizontal_list.to_dict(), + } + + +class VisualizablePage: + def __init__(self, name: str = None): + self._levels = [] + self.name = name + + def add_level(self, level: VisualizableLevel): + self._levels.append(level) + + def to_dict(self) -> Tuple[str, List[Dict]]: + return self.name, [level.to_dict() for level in self._levels] + + +class Visualizer(Plugin, metaclass=abc.ABCMeta): + Size = VisualizableSize + Color = VisualizableColor + Icon = VisualizableIcon + Alignment = VisualizableAlignment + + Base = VisualizableBase + Title = VisualizableTitle + Bool = VisualizableBool + VList = VisualizableVerticalList + HList = VisualizableHorizontalList + Table = VisualizableTable + + LevelSize = VisualizableLevelSize + Page = VisualizablePage + Level = VisualizableLevel + + @classmethod + @property + def python_base_path(cls): + return PythonModuleBasePaths.Visualizer.value + + @classmethod + @property + def report_model(cls): + return VisualizerReport + + @classmethod + @property + def config_model(cls) -> Type[VisualizerConfig]: + return VisualizerConfig + + def get_exceptions_to_catch(self) -> list: + return [ + VisualizerConfigurationException, + VisualizerRunException, + ] + + def before_run(self): + super().before_run() + logger.info(f"STARTED visualizer: {self.__repr__()}") + + def after_run_success(self, content): + if not isinstance(content, list): + raise VisualizerRunException( + f"Report has not correct type: {type(self.report.report)}" + ) + for elem in content: + if not isinstance(elem, tuple) or not isinstance(elem[1], list): + raise VisualizerRunException( + f"Report Page has not correct type: {type(elem)}" + ) + super().after_run_success(content) + for i, page in enumerate(content): + if i == 0: + report = self.report + else: + report = self.copy_report() + name, content = page + report.name = name + report.report = content + report.save() + + def after_run(self): + super().after_run() + logger.info(f"FINISHED visualizer: {self.__repr__()}") + + def copy_report(self) -> VisualizerReport: + report = self.report + report.pk = None + report.save() + return report + + def analyzer_reports(self) -> QuerySet: + from api_app.analyzers_manager.models import AnalyzerReport + + return AnalyzerReport.objects.filter(job=self._job) + + def connector_reports(self) -> QuerySet: + from api_app.connectors_manager.models import ConnectorReport + + return ConnectorReport.objects.filter(job=self._job) + + def pivots_reports(self) -> QuerySet: + from api_app.pivots_manager.models import PivotReport + + return PivotReport.objects.filter(job=self._job) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/decorators.py b/Submodules/IntelOwl/api_app/visualizers_manager/decorators.py new file mode 100644 index 0000000..7b9ccfa --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/decorators.py @@ -0,0 +1,45 @@ +from api_app.visualizers_manager.classes import ( + VisualizableBase, + VisualizableTitle, + logger, +) +from api_app.visualizers_manager.enums import VisualizableColor, VisualizableSize + + +# IMPORTANT! this function allows to handle the errors in the components render. +# You must define a function that returns a Visualizable then +# use this function as a decorator. Ex: +# @visualizable_error_handler_with_params("field1", VisualizableSize.S_2) +# def generate_field1(value): +# ... +# in case the generation of the field will raise an error +# this will handle it allowing to render the other components +def visualizable_error_handler_with_params( + *errors_names: str, + error_size: VisualizableSize = VisualizableSize.S_AUTO, +): + def visualizable_error_handler(func): + def wrapper(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as error: + logger.exception(error) + names = [func.__name__] if not errors_names else errors_names + result = [] + for error_name in names: + result.append( + VisualizableTitle( + title=VisualizableBase(value=error_name), + value=VisualizableBase( + value="error", color=VisualizableColor.DANGER + ), + size=error_size, + ) + ) + if len(result) == 1: + return result[0] + return result + + return wrapper + + return visualizable_error_handler diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/enums.py b/Submodules/IntelOwl/api_app/visualizers_manager/enums.py new file mode 100644 index 0000000..46f2624 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/enums.py @@ -0,0 +1,125 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import enum + + +class VisualizableLevelSize(enum.Enum): + """Level sizes in Page elements (same of html headers)""" + + S_1 = "1" + S_2 = "2" + S_3 = "3" + S_4 = "4" + S_5 = "5" + S_6 = "6" + + def __str__(self): + return self.value + + +class VisualizableSize(enum.Enum): + """Horizontal sizes of Visualizable elements (same of bootstrap)""" + + S_1 = "1" + S_2 = "2" + S_3 = "3" + S_4 = "4" + S_5 = "5" + S_6 = "6" + S_7 = "7" + S_8 = "8" + S_9 = "9" + S_10 = "10" + S_11 = "11" + S_ALL = "12" + S_AUTO = "auto" + + def __str__(self): + return self.value + + +class VisualizableColor(enum.Enum): + PRIMARY = "primary" + SECONDARY = "secondary" + TERTIARY = "tertiary" + SUCCESS = "success" + DANGER = "danger" + WARNING = "warning" + INFO = "info" + DARK = "dark" + WHITE = "white" + TRANSPARENT = "" + + def __str__(self): + return self.value + + def __bool__(self): + if self is self.TRANSPARENT: + return False + return True + + +class VisualizableIcon(enum.Enum): + BOOK = "book" + INFO = "info" + LIKE = "like" + DISLIKE = "dislike" + HEART = "heart" + MALWARE = "malware" + WARNING = "warning" + SHIELD = "shield" + FIRE = "fire" + ALARM = "alarm" + MAGNIFYING_GLASS = "magnifyingGlass" + CREDIT_CARD = "creditCard" + EMAIL = "email" + PHISHING = "hook" + FILTER = "filter" + INCOGNITO = "incognito" + INBOX = "inbox" + CLOUD_UPLOAD = "cloudUpload" + CLOUD_SYNC = "cloudSync" + LIGHTHOUSE_ON = "lighthouseOn" + CONTROLLER = "controller" + EXIT = "exit" + CONNECTION = "connection" + LOCK = "lock" + LOCK_OPEN = "lockOpen" + VMWare = "vmware" + VIRTUAL_HOST = "virtualHost" + NETWORK_NODE = "networkNode" + # external services + OTX = "otx" + GITHUB = "github" + VIRUSTotal = "virusTotal" + TWITTER = "twitter" + QUOKKA = "quokka" + HYBRIDAnalysis = "hybridAnalysis" + URLHAUS = "urlhaus" + GOOGLE = "google" + CLOUDFLARE = "cloudflare" + QUAD_9 = "quad9" + + EMPTY = "" + + def __bool__(self): + if self is self.EMPTY: + return False + return True + + +class VisualizableAlignment(enum.Enum): + """Alignmnets for VisualizableHorizontalList elements (same of bootstrap)""" + + START = "start" + CENTER = "center" + END = "end" + BETWEEN = "between" + AROUND = "around" + + def __str__(self): + return self.value + + def __bool__(self): + return True diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/exceptions.py b/Submodules/IntelOwl/api_app/visualizers_manager/exceptions.py new file mode 100644 index 0000000..b387bbb --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/exceptions.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + + +class NotRunnableVisualizer(Exception): + pass + + +class VisualizerConfigurationException(Exception): + pass + + +class VisualizerRunException(Exception): + pass diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/forms.py b/Submodules/IntelOwl/api_app/visualizers_manager/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0001_initial_squashed.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0001_initial_squashed.py new file mode 100644 index 0000000..3126d3d --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0001_initial_squashed.py @@ -0,0 +1,220 @@ +# Generated by Django 4.2.8 on 2024-02-08 13:48 + +import django.contrib.postgres.fields +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +from django.db import migrations, models + +import api_app.visualizers_manager.validators + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("api_app", "0001_1_initial_squashed"), + ("playbooks_manager", "0001_initial_squashed"), + ] + + operations = [ + migrations.CreateModel( + name="VisualizerConfig", + fields=[ + ( + "name", + models.CharField( + max_length=50, + primary_key=True, + serialize=False, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", + "Your name should match the [A-Za-z0-9_] characters", + ) + ], + ), + ), + ( + "python_module", + models.ForeignKey( + limit_choices_to={ + "base_path": "api_app.visualizers_manager.visualizers" + }, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)ss", + to="api_app.pythonmodule", + ), + ), + ("description", models.TextField()), + ("disabled", models.BooleanField(default=False)), + ( + "playbooks", + models.ManyToManyField( + "playbooks_manager.playbookconfig", + related_name="visualizers", + ), + ), + ( + "disabled_in_organizations", + models.ManyToManyField( + blank=True, + related_name="%(app_label)s_%(class)s_disabled", + to="certego_saas_organization.organization", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="VisualizerReport", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("parameters", models.JSONField(default={}, editable=False)), + ( + "name", + models.CharField( + blank=True, default=None, max_length=50, null=True + ), + ), + ( + "config", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="visualizers_manager.visualizerconfig", + ), + ), + ( + "status", + models.CharField( + choices=[ + ("FAILED", "Failed"), + ("PENDING", "Pending"), + ("RUNNING", "Running"), + ("SUCCESS", "Success"), + ("KILLED", "Killed"), + ], + max_length=50, + ), + ), + ( + "report", + models.JSONField( + default=list, + validators=[ + api_app.visualizers_manager.validators.validate_report + ], + ), + ), + ( + "errors", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=512), + blank=True, + default=list, + size=None, + ), + ), + ("start_time", models.DateTimeField(default=django.utils.timezone.now)), + ("end_time", models.DateTimeField(default=django.utils.timezone.now)), + ("task_id", models.UUIDField()), + ( + "job", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="%(class)ss", + to="api_app.job", + ), + ), + ], + ), + migrations.AddIndex( + model_name="visualizerconfig", + index=models.Index( + fields=["python_module", "disabled"], + name="visualizers_python__2c4ded_idx", + ), + ), + migrations.AlterModelOptions( + name="visualizerreport", + options={"ordering": ["pk"]}, + ), + migrations.AlterModelOptions( + name="visualizerconfig", + options={"ordering": ["name", "disabled"]}, + ), + migrations.RenameIndex( + model_name="visualizerconfig", + new_name="visualizers_python__8b1832_idx", + old_name="visualizers_python__2c4ded_idx", + ), + migrations.AddField( + model_name="visualizerconfig", + name="health_check_status", + field=models.BooleanField(default=True, editable=False), + ), + migrations.AddField( + model_name="visualizerconfig", + name="health_check_task", + field=models.OneToOneField( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="healthcheck_for_%(class)s", + to="django_celery_beat.periodictask", + ), + ), + migrations.AddField( + model_name="visualizerconfig", + name="routing_key", + field=models.CharField(default="default", max_length=50), + ), + migrations.AddField( + model_name="visualizerconfig", + name="soft_time_limit", + field=models.IntegerField( + default=60, validators=[django.core.validators.MinValueValidator(0)] + ), + ), + migrations.AddField( + model_name="visualizerreport", + name="sent_to_bi", + field=models.BooleanField(default=False, editable=False), + ), + migrations.AlterField( + model_name="visualizerconfig", + name="python_module", + field=models.ForeignKey( + limit_choices_to={ + "base_path__in": ["api_app.visualizers_manager.visualizers"] + }, + on_delete=django.db.models.deletion.PROTECT, + related_name="%(class)ss", + to="api_app.pythonmodule", + ), + ), + migrations.AlterField( + model_name="visualizerreport", + name="parameters", + field=models.JSONField(editable=False), + ), + migrations.AddIndex( + model_name="visualizerreport", + index=models.Index( + fields=["sent_to_bi", "-start_time"], name="visualizerreportsBISearch" + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0000_visualizer_config_dns.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0000_visualizer_config_dns.py new file mode 100644 index 0000000..8c6a47d --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0000_visualizer_config_dns.py @@ -0,0 +1,97 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "DNS", + "python_module": { + "module": "dns.DNS", + "base_path": "api_app.visualizers_manager.visualizers", + }, + "description": "Visualize information about DNS resolvers and DNS malicious detectors", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "health_check_task": None, + "playbooks": ["Dns"], + "model": "visualizers_manager.VisualizerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("visualizers_manager", "0001_initial_squashed"), + ("playbooks_manager", "0002_0004_playbook_config_sample_static_analysis"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0001_visualizer_config_domain_reputation.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0001_visualizer_config_domain_reputation.py new file mode 100644 index 0000000..4128a4a --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0001_visualizer_config_domain_reputation.py @@ -0,0 +1,96 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Domain_Reputation", + "python_module": { + "module": "domain_reputation_services.DomainReputationServices", + "base_path": "api_app.visualizers_manager.visualizers", + }, + "description": 'Visualizer for the Playbook "Popular_URL_Reputation_Services"', + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "health_check_task": None, + "playbooks": ["Popular_URL_Reputation_Services"], + "model": "visualizers_manager.VisualizerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("visualizers_manager", "0002_0000_visualizer_config_dns"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0002_visualizer_config_ip_reputation.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0002_visualizer_config_ip_reputation.py new file mode 100644 index 0000000..9be9140 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0002_visualizer_config_ip_reputation.py @@ -0,0 +1,96 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "IP_Reputation", + "python_module": { + "module": "ip_reputation_services.IPReputationServices", + "base_path": "api_app.visualizers_manager.visualizers", + }, + "description": 'Visualizer for the Playbook "Popular_IP_Reputation_Services"', + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "health_check_task": None, + "playbooks": ["Popular_IP_Reputation_Services"], + "model": "visualizers_manager.VisualizerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("visualizers_manager", "0002_0001_visualizer_config_domain_reputation"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0003_visualizer_config_pivot.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0003_visualizer_config_pivot.py new file mode 100644 index 0000000..a2bb20f --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0003_visualizer_config_pivot.py @@ -0,0 +1,96 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Pivot", + "python_module": { + "module": "pivot.Pivot", + "base_path": "api_app.visualizers_manager.visualizers", + }, + "description": "Visualizer for pivot", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "health_check_task": None, + "playbooks": [], + "model": "visualizers_manager.VisualizerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("visualizers_manager", "0002_0002_visualizer_config_ip_reputation"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0004_visualizer_config_yara.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0004_visualizer_config_yara.py new file mode 100644 index 0000000..8f12429 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0002_0004_visualizer_config_yara.py @@ -0,0 +1,96 @@ +from django.db import migrations +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ForwardOneToOneDescriptor, + ManyToManyDescriptor, +) + +plugin = { + "name": "Yara", + "python_module": { + "module": "yara.Yara", + "base_path": "api_app.visualizers_manager.visualizers", + }, + "description": "Visualize information about yara matches", + "disabled": False, + "soft_time_limit": 60, + "routing_key": "default", + "health_check_status": True, + "health_check_task": None, + "playbooks": ["Sample_Static_Analysis"], + "model": "visualizers_manager.VisualizerConfig", +} + +params = [] + +values = [] + + +def _get_real_obj(Model, field, value): + if ( + type(getattr(Model, field)) + in [ForwardManyToOneDescriptor, ForwardOneToOneDescriptor] + and value + ): + other_model = getattr(Model, field).get_queryset().model + # in case is a dictionary, we have to retrieve the object with every key + if isinstance(value, dict): + real_vals = {} + for key, real_val in value.items(): + real_vals[key] = _get_real_obj(other_model, key, real_val) + value = other_model.objects.get_or_create(**real_vals)[0] + # it is just the primary key serialized + else: + value = other_model.objects.get(pk=value) + return value + + +def _create_object(Model, data): + mtm, no_mtm = {}, {} + for field, value in data.items(): + if type(getattr(Model, field)) is ManyToManyDescriptor: + mtm[field] = value + else: + value = _get_real_obj(Model, field, value) + no_mtm[field] = value + try: + o = Model.objects.get(**no_mtm) + except Model.DoesNotExist: + o = Model(**no_mtm) + o.full_clean() + o.save() + for field, value in mtm.items(): + attribute = getattr(o, field) + attribute.set(value) + return False + return True + + +def migrate(apps, schema_editor): + Parameter = apps.get_model("api_app", "Parameter") + PluginConfig = apps.get_model("api_app", "PluginConfig") + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + if not Model.objects.filter(name=plugin["name"]).exists(): + exists = _create_object(Model, plugin) + if not exists: + for param in params: + _create_object(Parameter, param) + for value in values: + _create_object(PluginConfig, value) + + +def reverse_migrate(apps, schema_editor): + python_path = plugin.pop("model") + Model = apps.get_model(*python_path.split(".")) + Model.objects.get(name=plugin["name"]).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("visualizers_manager", "0002_0003_visualizer_config_pivot"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] + atomic = False diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_1_change_primary_key.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_1_change_primary_key.py new file mode 100644 index 0000000..5ab100b --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_1_change_primary_key.py @@ -0,0 +1,11 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("visualizers_manager", "0002_0004_visualizer_config_yara"), + ("api_app", "0001_2_initial_squashed"), + ] + + operations = [] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_2_change_primary_key.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_2_change_primary_key.py new file mode 100644 index 0000000..c67168a --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_2_change_primary_key.py @@ -0,0 +1,59 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + VisualizerConfig = apps.get_model("visualizers_manager", "VisualizerConfig") + VisualizerConfig.objects.update( + playbooks2=ArraySubquery( + VisualizerConfig.objects.filter(pk=models.OuterRef("pk")).values( + "playbooks__name" + ) + ), + disabled2=ArraySubquery( + VisualizerConfig.objects.filter(pk=models.OuterRef("pk")).values( + "disabled_in_organizations__pk" + ) + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("visualizers_manager", "0036_1_change_primary_key"), + ] + + operations = [ + migrations.AlterField( + model_name="visualizerreport", + name="config", + field=models.CharField(max_length=100, null=False, blank=False), + ), + migrations.AddField( + model_name="visualizerconfig", + name="playbooks2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.AddField( + model_name="visualizerconfig", + name="disabled2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.IntegerField(), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate), + migrations.RemoveField(model_name="visualizerconfig", name="playbooks"), + migrations.RemoveField( + model_name="visualizerconfig", name="disabled_in_organizations" + ), + ] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_3_change_primary_key.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_3_change_primary_key.py new file mode 100644 index 0000000..4277ebd --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_3_change_primary_key.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("visualizers_manager", "0036_2_change_primary_key"), + ("api_app", "0057_2_change_primary_key"), + ] + + operations = [ + migrations.RunSQL( + 'ALTER TABLE "visualizers_manager_visualizerconfig" DROP CONSTRAINT "visualizers_manager_visualizerconfig_pkey" CASCADE;' + ), + migrations.AlterField( + model_name="visualizerconfig", + name="name", + field=models.CharField( + max_length=100, + unique=True, + primary_key=False, + validators=[ + django.core.validators.RegexValidator( + "^\\w+$", "Your name should match the [A-Za-z0-9_] characters" + ) + ], + ), + ), + migrations.AddField( + model_name="visualizerconfig", + name="id", + field=models.BigAutoField( + auto_created=True, serialize=False, verbose_name="ID", primary_key=True + ), + ), + ] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_4_change_primary_key.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_4_change_primary_key.py new file mode 100644 index 0000000..b9260cd --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0036_4_change_primary_key.py @@ -0,0 +1,77 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django +from django.db import migrations, models + + +def migrate(apps, schema_editor): + VisualizerReport = apps.get_model("visualizers_manager", "VisualizerReport") + VisualizerConfig = apps.get_model("visualizers_manager", "VisualizerConfig") + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + Organization = apps.get_model("certego_saas_organization", "Organization") + name = VisualizerConfig.objects.filter( + name=models.OuterRef("old_config") + ).values_list("pk")[:1] + VisualizerReport.objects.update(config=models.Subquery(name)) + for config in VisualizerConfig.objects.all(): + config.playbooks.set(PlaybookConfig.objects.filter(name__in=config.playbooks2)) + if config.disabled2: + ContentType = apps.get_model("contenttypes", "ContentType") + ct = ContentType.objects.get_for_model(config) + OrganizationPluginConfiguration = apps.get_model( + "api_app", "OrganizationPluginConfiguration" + ) + for org in config.disabled2: + if org: + OrganizationPluginConfiguration.objects.create( + organization=Organization.objects.get(pk=org), + object_id=config.pk, + content_type=ct, + disabled=True, + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("api_app", "0001_2_initial_squashed"), + ("visualizers_manager", "0036_3_change_primary_key"), + ("playbooks_manager", "0001_initial_squashed"), + ] + + operations = [ + migrations.RenameField( + model_name="visualizerreport", old_name="config", new_name="old_config" + ), + migrations.AddField( + model_name="visualizerreport", + name="config", + field=models.ForeignKey( + default=None, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="visualizers_manager.visualizerconfig", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="visualizerconfig", + name="playbooks", + field=models.ManyToManyField( + related_name="visualizers", + to="playbooks_manager.PlaybookConfig", + ), + ), + migrations.RunPython(migrate), + migrations.AlterField( + model_name="visualizerreport", + name="config", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="reports", + to="visualizers_manager.visualizerconfig", + ), + ), + migrations.RemoveField(model_name="visualizerreport", name="old_config"), + migrations.RemoveField(model_name="visualizerconfig", name="playbooks2"), + migrations.RemoveField(model_name="visualizerconfig", name="disabled2"), + ] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0037_2_change_primary_key.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0037_2_change_primary_key.py new file mode 100644 index 0000000..d5c5b42 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0037_2_change_primary_key.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +import django.contrib.postgres.fields +from django.contrib.postgres.expressions import ArraySubquery +from django.db import migrations, models + + +def migrate(apps, schema_editor): + VisualizerConfig = apps.get_model("visualizers_manager", "VisualizerConfig") + VisualizerConfig.objects.update( + playbooks2=ArraySubquery( + VisualizerConfig.objects.filter(pk=models.OuterRef("pk")).values( + "playbooks__name" + ) + ), + ) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_1_change_primary_key"), + ("visualizers_manager", "0036_4_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="visualizerconfig", + name="playbooks2", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=100), + blank=True, + default=list, + size=None, + ), + ), + migrations.RunPython(migrate), + migrations.RemoveField(model_name="visualizerconfig", name="playbooks"), + ] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0037_4_change_primary_key.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0037_4_change_primary_key.py new file mode 100644 index 0000000..d1608aa --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/0037_4_change_primary_key.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.8 on 2024-01-09 14:31 +from django.db import migrations, models + + +def migrate(apps, schema_editor): + VisualizerConfig = apps.get_model("visualizers_manager", "VisualizerConfig") + PlaybookConfig = apps.get_model("playbooks_manager", "PlaybookConfig") + for config in VisualizerConfig.objects.all(): + playbooks = PlaybookConfig.objects.filter(name__in=config.playbooks2) + config.playbooks.set(playbooks.values_list("pk", flat=True)) + + +class Migration(migrations.Migration): + dependencies = [ + ("playbooks_manager", "0024_3_change_primary_key"), + ("visualizers_manager", "0037_2_change_primary_key"), + ] + + operations = [ + migrations.AddField( + model_name="visualizerconfig", + name="playbooks", + field=models.ManyToManyField( + related_name="visualizers", + to="playbooks_manager.PlaybookConfig", + ), + ), + migrations.RunPython( + migrate, + ), + migrations.RemoveField(model_name="visualizerconfig", name="playbooks2"), + ] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/migrations/__init__.py b/Submodules/IntelOwl/api_app/visualizers_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/models.py b/Submodules/IntelOwl/api_app/visualizers_manager/models.py new file mode 100644 index 0000000..c6421ad --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/models.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from django.contrib.contenttypes.fields import GenericRelation +from django.db import models + +from api_app.choices import PythonModuleBasePaths +from api_app.models import AbstractReport, PythonConfig, PythonModule +from api_app.playbooks_manager.models import PlaybookConfig +from api_app.visualizers_manager.exceptions import VisualizerConfigurationException +from api_app.visualizers_manager.queryset import VisualizerReportQuerySet +from api_app.visualizers_manager.validators import validate_report + + +class VisualizerReport(AbstractReport): + objects = VisualizerReportQuerySet.as_manager() + config = models.ForeignKey( + "VisualizerConfig", related_name="reports", null=False, on_delete=models.CASCADE + ) + report = models.JSONField(default=list, validators=[validate_report]) + name = models.CharField(null=True, blank=True, default=None, max_length=50) + + class Meta: + ordering = ["pk"] + indexes = AbstractReport.Meta.indexes + + +class VisualizerConfig(PythonConfig): + playbooks = models.ManyToManyField( + PlaybookConfig, + related_name="visualizers", + ) + python_module = models.ForeignKey( + PythonModule, + on_delete=models.PROTECT, + related_name="%(class)ss", + limit_choices_to={ + "base_path__in": [ + PythonModuleBasePaths.Visualizer.value, + ] + }, + ) + orgs_configuration = GenericRelation( + "api_app.OrganizationPluginConfiguration", related_name="%(class)s" + ) + + @classmethod + @property + def plugin_type(cls) -> str: + return "3" + + @classmethod + @property + def config_exception(cls): + return VisualizerConfigurationException + + @classmethod + @property + def serializer_class(cls): + from api_app.visualizers_manager.serializers import VisualizerConfigSerializer + + return VisualizerConfigSerializer diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/queryset.py b/Submodules/IntelOwl/api_app/visualizers_manager/queryset.py new file mode 100644 index 0000000..8566102 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/queryset.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING, Type + +from api_app.queryset import AbstractReportQuerySet + +if TYPE_CHECKING: + from api_app.visualizers_manager.serializers import VisualizerReportBISerializer + + +class VisualizerReportQuerySet(AbstractReportQuerySet): + @classmethod + def _get_bi_serializer_class(cls) -> Type["VisualizerReportBISerializer"]: + from api_app.visualizers_manager.serializers import VisualizerReportBISerializer + + return VisualizerReportBISerializer diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/serializers.py b/Submodules/IntelOwl/api_app/visualizers_manager/serializers.py new file mode 100644 index 0000000..ff18836 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/serializers.py @@ -0,0 +1,65 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from rest_framework import serializers as rfs + +from ..playbooks_manager.models import PlaybookConfig +from ..serializers.plugin import ( + PythonConfigSerializer, + PythonConfigSerializerForMigration, +) +from ..serializers.report import AbstractReportBISerializer, AbstractReportSerializer +from .models import VisualizerConfig, VisualizerReport + + +class VisualizerConfigSerializer(PythonConfigSerializer): + playbooks = rfs.SlugRelatedField( + many=True, queryset=PlaybookConfig.objects.all(), slug_field="name" + ) + + class Meta: + model = VisualizerConfig + exclude = PythonConfigSerializer.Meta.exclude + list_serializer_class = PythonConfigSerializer.Meta.list_serializer_class + + +class VisualizerConfigSerializerForMigration(PythonConfigSerializerForMigration): + playbooks = rfs.SlugRelatedField( + queryset=PlaybookConfig.objects.all(), slug_field="name", many=True + ) + + class Meta: + model = VisualizerConfig + exclude = PythonConfigSerializerForMigration.Meta.exclude + + +class VisualizerReportSerializer(AbstractReportSerializer): + name = rfs.SerializerMethodField() + + config = rfs.SlugRelatedField( + queryset=VisualizerConfig.objects.all(), slug_field="name" + ) + + @classmethod + def get_name(cls, instance: VisualizerReport): + return instance.name or instance.config.name + + class Meta: + model = VisualizerReport + fields = AbstractReportSerializer.Meta.fields + [ + "config", + ] + list_serializer_class = AbstractReportSerializer.Meta.list_serializer_class + + +class VisualizerReportBISerializer(AbstractReportBISerializer): + name = rfs.SerializerMethodField() + + @classmethod + def get_name(cls, instance: VisualizerReport): + return instance.name or instance.config.pk + + class Meta: + model = VisualizerReport + fields = AbstractReportBISerializer.Meta.fields + list_serializer_class = AbstractReportBISerializer.Meta.list_serializer_class diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/signals.py b/Submodules/IntelOwl/api_app/visualizers_manager/signals.py new file mode 100644 index 0000000..ada644f --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/signals.py @@ -0,0 +1,31 @@ +import logging +import uuid + +from django.conf import settings +from django.dispatch import receiver + +from api_app.signals import migrate_finished +from api_app.visualizers_manager.models import VisualizerConfig +from intel_owl.celery import get_queue_name + +logger = logging.getLogger(__name__) + + +@receiver(migrate_finished) +def post_migrate_visualizers_manager( + sender, + *args, + check_unapplied: bool = False, + **kwargs, +): + logger.info(f"Post migrate {args} {kwargs}") + if check_unapplied: + return + from intel_owl.tasks import refresh_cache + + refresh_cache.apply_async( + queue=get_queue_name(settings.CONFIG_QUEUE), + MessageGroupId=str(uuid.uuid4()), + priority=3, + args=[VisualizerConfig.python_path], + ) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/urls.py b/Submodules/IntelOwl/api_app/visualizers_manager/urls.py new file mode 100644 index 0000000..5cea5c2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/urls.py @@ -0,0 +1,24 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +# Routers provide an easy way of automatically determining the URL conf. +from api_app.visualizers_manager.views import ( + VisualizerActionViewSet, + VisualizerConfigViewSet, +) + +router = routers.DefaultRouter(trailing_slash=False) +router.register( + r"jobs/(?P\d+)/visualizer/(?P\w+)", + VisualizerActionViewSet, + basename="visualizerreport", +) +router.register(r"visualizer", VisualizerConfigViewSet, basename="visualizer") + +urlpatterns = [ + # Viewsets + path(r"", include(router.urls)), +] diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/validators.py b/Submodules/IntelOwl/api_app/visualizers_manager/validators.py new file mode 100644 index 0000000..5fb3b41 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/validators.py @@ -0,0 +1,29 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from api_app.validators import validate_schema +from api_app.visualizers_manager.choices import Position + + +def validate_report(value): + schema = { + "type": "object", + "title": "Report", + "patternProperties": { + "^[A-Za-z][A-Za-z0-9_]*$": { + "type": "object", + "properties": { + "value": {}, + "position": {"enum": list(Position.values)}, + "priority": { + "type": "integer", + "minimum": 1, + "maximum": 10, + }, + }, + "additionalProperties": False, + } + }, + "additionalProperties": False, + } + return validate_schema(value, schema) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/views.py b/Submodules/IntelOwl/api_app/visualizers_manager/views.py new file mode 100644 index 0000000..71e9277 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/views.py @@ -0,0 +1,26 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging + +from api_app.views import PythonConfigViewSet, PythonReportActionViewSet +from api_app.visualizers_manager.models import VisualizerReport +from api_app.visualizers_manager.serializers import VisualizerConfigSerializer + +logger = logging.getLogger(__name__) + + +__all__ = [ + "VisualizerConfigViewSet", +] + + +class VisualizerConfigViewSet(PythonConfigViewSet): + serializer_class = VisualizerConfigSerializer + + +class VisualizerActionViewSet(PythonReportActionViewSet): + @classmethod + @property + def report_model(cls): + return VisualizerReport diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/__init__.py b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/dns.py b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/dns.py new file mode 100644 index 0000000..006437f --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/dns.py @@ -0,0 +1,185 @@ +from logging import getLogger +from typing import Dict, List + +# ignore flake line too long in imports +from api_app.analyzers_manager.models import AnalyzerConfig, AnalyzerReport +from api_app.analyzers_manager.observable_analyzers.dns.dns_malicious_detectors.cloudflare_malicious_detector import ( # noqa: E501 + CloudFlareMaliciousDetector, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_malicious_detectors.dns0_eu_malicious_detector import ( # noqa: E501 + DNS0EUMaliciousDetector, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_malicious_detectors.quad9_malicious_detector import ( # noqa: E501 + Quad9MaliciousDetector, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_resolvers.classic_dns_resolver import ( # noqa: E501 + ClassicDNSResolver, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_resolvers.cloudflare_dns_resolver import ( # noqa: E501 + CloudFlareDNSResolver, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_resolvers.dns0_eu_resolver import ( # noqa: E501 + DNS0EUResolver, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_resolvers.google_dns_resolver import ( # noqa: E501 + GoogleDNSResolver, +) +from api_app.analyzers_manager.observable_analyzers.dns.dns_resolvers.quad9_dns_resolver import ( # noqa: E501 + Quad9DNSResolver, +) +from api_app.choices import ObservableClassification +from api_app.models import Job +from api_app.visualizers_manager.classes import VisualizableObject, Visualizer +from api_app.visualizers_manager.decorators import ( + visualizable_error_handler_with_params, +) + +logger = getLogger(__name__) + + +class DNS(Visualizer): + @classmethod + @property + def first_level_analyzers(cls) -> List[str]: + return [ # noqa + ClassicDNSResolver.python_module, + CloudFlareDNSResolver.python_module, + GoogleDNSResolver.python_module, + DNS0EUResolver.python_module, + Quad9DNSResolver.python_module, + ] + + @classmethod + @property + def second_level_analyzers(cls) -> List[str]: + return [ # noqa + CloudFlareMaliciousDetector.python_module, + DNS0EUMaliciousDetector.python_module, + Quad9MaliciousDetector.python_module, + ] + + @visualizable_error_handler_with_params() + def _dns_resolution(self, analyzer_report: AnalyzerReport) -> VisualizableObject: + printable_analyzer_name = analyzer_report.config.name.replace("_", " ") + logger.debug(f"{printable_analyzer_name=}") + disable_element = not analyzer_report.report["resolutions"] + return self.VList( + name=self.Base(value=f"{printable_analyzer_name}", disable=disable_element), + value=[ + self.Base( + value=dns_resolution["data"] + if self._job.observable_classification + == ObservableClassification.DOMAIN + else dns_resolution, + disable=False, + ) + for dns_resolution in analyzer_report.report["resolutions"] + ], + size=self.Size.S_2, + disable=disable_element, + start_open=True, + ) + + @visualizable_error_handler_with_params() + def _dns_block(self, analyzer_report: AnalyzerReport) -> VisualizableObject: + printable_analyzer_name = analyzer_report.config.name.replace("_", " ") + logger.debug(f"{printable_analyzer_name=}") + return self.Bool( + value=printable_analyzer_name, + disable=not analyzer_report.report["malicious"], + ) + + def run(self) -> List[Dict]: + first_level_elements = [] + second_level_elements = [] + + for analyzer_report in self.analyzer_reports(): + if "dns.dns_resolvers" in analyzer_report.config.python_module: + first_level_elements.append( + self._dns_resolution(analyzer_report=analyzer_report) + ) + else: + second_level_elements.append( + self._dns_block(analyzer_report=analyzer_report) + ) + + page = self.Page(name="DNS") + page.add_level( + self.Level( + position=1, + size=self.LevelSize.S_3, + horizontal_list=self.HList(value=first_level_elements), + ) + ) + page.add_level( + self.Level( + position=2, + size=self.LevelSize.S_5, + horizontal_list=self.HList(value=second_level_elements), + ) + ) + logger.debug(f"levels: {page.to_dict()}") + return [page.to_dict()] + + @classmethod + def _monkeypatch(cls): + from kombu import uuid + + # malicious detector services (1st level) + + for python_module in cls.first_level_analyzers: + try: + AnalyzerReport.objects.get( + config=AnalyzerConfig.objects.get(python_module=python_module), + job=Job.objects.first(), + status=AnalyzerReport.Status.SUCCESS, + ) + except AnalyzerReport.DoesNotExist: + report = AnalyzerReport( + config=AnalyzerConfig.objects.get(python_module=python_module), + job=Job.objects.first(), + status=AnalyzerReport.Status.SUCCESS, + report={ + "observable": "dns.google.com", + "resolutions": [ + { + "TTL": 456, + "data": "8.8.8.8", + "name": "dns.google.com", + "type": 1, + }, + { + "TTL": 456, + "data": "8.8.4.4", + "name": "dns.google.com", + "type": 1, + }, + ], + }, + task_id=uuid(), + parameters={}, + ) + report.full_clean() + report.save() + + # classic DNS resolution (2nd level) + for python_module in cls.second_level_analyzers: + try: + AnalyzerReport.objects.get( + config=AnalyzerConfig.objects.get(python_module=python_module), + job=Job.objects.first(), + ) + except AnalyzerReport.DoesNotExist: + report = AnalyzerReport( + config=AnalyzerConfig.objects.get(python_module=python_module), + job=Job.objects.first(), + status=AnalyzerReport.Status.SUCCESS, + report={"observable": "dns.google.com", "malicious": False}, + task_id=uuid(), + parameters={}, + ) + report.full_clean() + report.save() + + patches = [] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/domain_reputation_services.py b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/domain_reputation_services.py new file mode 100644 index 0000000..74521a2 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/domain_reputation_services.py @@ -0,0 +1,287 @@ +from logging import getLogger +from typing import Dict, List + +from django.db.models import Q + +from api_app.analyzers_manager.models import AnalyzerReport +from api_app.choices import ReportStatus +from api_app.visualizers_manager.classes import Visualizer +from api_app.visualizers_manager.decorators import ( + visualizable_error_handler_with_params, +) +from api_app.visualizers_manager.enums import VisualizableIcon + +logger = getLogger(__name__) + + +class DomainReputationServices(Visualizer): + @classmethod + def update(cls) -> bool: + pass + + @visualizable_error_handler_with_params("VirusTotal") + def _vt3(self): + try: + analyzer_report = self.analyzer_reports().get( + config__name="VirusTotal_v3_Get_Observable" + ) + except AnalyzerReport.DoesNotExist: + logger.warning("VirusTotal_v3_Get_Observable report does not exist") + virustotal_report = self.Title( + self.Base( + value="VirusTotal", + link="", + icon=VisualizableIcon.VIRUSTotal, + ), + self.Base(value="Engine Hits: Unknown"), + disable=True, + ) + return virustotal_report + else: + hits = ( + analyzer_report.report.get("data", {}) + .get("attributes", {}) + .get("last_analysis_stats", {}) + .get("malicious", 0) + ) + virustotal_report = self.Title( + self.Base( + value="VirusTotal", + link=analyzer_report.report.get("link", ""), + icon=VisualizableIcon.VIRUSTotal, + ), + self.Base(value=f"Engine Hits: {hits}"), + disable=analyzer_report.status != ReportStatus.SUCCESS or not hits, + ) + return virustotal_report + + @visualizable_error_handler_with_params("URLhaus") + def _urlhaus(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="URLhaus") + except AnalyzerReport.DoesNotExist: + logger.warning("URLhaus report does not exist") + else: + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or analyzer_report.report.get("query_status", None) != "ok" + ) + urlhaus_report = self.Title( + self.Base( + value="URLhaus", + link=analyzer_report.report.get("urlhaus_reference", ""), + icon=VisualizableIcon.URLHAUS, + ), + self.Base( + value="" + if disabled + else f'found {analyzer_report.report.get("urlhaus_status", "")}' + ), + disable=disabled, + ) + return urlhaus_report + + @visualizable_error_handler_with_params("ThreatFox") + def _threatfox(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="ThreatFox") + except AnalyzerReport.DoesNotExist: + logger.warning("Threatfox report does not exist") + else: + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or analyzer_report.report.get("query_status", None) != "ok" + ) + data = analyzer_report.report.get("data", []) + malware_printable = "" + if data and isinstance(data, list): + malware_printable = data[0].get("malware_printable", "") + threatfox_report = self.Title( + self.Base( + value="ThreatFox", + link=analyzer_report.report.get("link", ""), + icon=VisualizableIcon.URLHAUS, + ), + self.Base(value="" if disabled else f"found {malware_printable}"), + disable=disabled, + ) + return threatfox_report + + @visualizable_error_handler_with_params("Tranco") + def _tranco(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="Tranco") + except AnalyzerReport.DoesNotExist: + logger.warning("Tranco report does not exist") + else: + ranks = analyzer_report.report.get("ranks", []) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not ranks + rank = "" + if ranks and isinstance(ranks, list): + rank = ranks[0].get("rank", "") + tranco_report = self.Title( + self.Base( + value="Tranco Rank", + link="https://tranco-list.eu/", + ), + self.Base(value="" if disabled else rank), + disable=disabled, + ) + return tranco_report + + @visualizable_error_handler_with_params("Phishtank") + def _phishtank(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="Phishtank") + except AnalyzerReport.DoesNotExist: + logger.warning("Phishtank report does not exist") + else: + results = analyzer_report.report.get("results", {}) + in_database = results.get("in_database", False) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not in_database + phishtank_report = self.Title( + self.Base( + value="Phishtank", + link=results.get("phish_detail_page", ""), + icon=VisualizableIcon.PHISHING, + ), + self.Base(value="" if disabled else "found"), + disable=disabled, + ) + return phishtank_report + + @visualizable_error_handler_with_params("PhishingArmy") + def _phishing_army(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="PhishingArmy") + except AnalyzerReport.DoesNotExist: + logger.warning("PhishingArmy report does not exist") + else: + found = analyzer_report.report.get("found", False) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not found + phishtank_report = self.Title( + self.Base( + value="PhishingArmy", + link=analyzer_report.report.get("link", ""), + icon=VisualizableIcon.PHISHING, + ), + self.Base(value="" if disabled else "found"), + disable=disabled, + ) + return phishtank_report + + @visualizable_error_handler_with_params("InQuest") + def _inquest_repdb(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="InQuest_REPdb") + except AnalyzerReport.DoesNotExist: + logger.warning("InQuest_REPdb report does not exist") + else: + success = analyzer_report.report.get("success", False) + data = analyzer_report.report.get("data", []) + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or not success + or not data + ) + inquest_report = self.Title( + self.Base( + value="InQuest", + link=analyzer_report.report.get("link", ""), + icon=VisualizableIcon.WARNING, + ), + self.Base(value="" if disabled else "found"), + disable=disabled, + ) + return inquest_report + + @visualizable_error_handler_with_params("OTX Alienvault") + def _otxquery(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="OTXQuery") + except AnalyzerReport.DoesNotExist: + logger.warning("OTXQuery report does not exist") + else: + pulses = analyzer_report.report.get("pulses", []) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not pulses + otx_report = self.VList( + name=self.Base( + value="OTX Alienvault", icon=VisualizableIcon.OTX, disable=disabled + ), + value=[ + self.Base( + value=p.get("name", ""), + link=p.get("link", ""), + disable=disabled, + ) + for p in pulses + ], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + ) + return otx_report + + def run(self) -> List[Dict]: + first_level_elements = [] + second_level_elements = [] + third_level_elements = [] + + for analyzer_report in self.analyzer_reports().filter( + Q(config__name__endswith="Malicious_Detector") + | Q(config__name="GoogleSafebrowsing") + ): + printable_analyzer_name = analyzer_report.config.name.replace("_", " ") + third_level_elements.append( + self.Bool( + value=printable_analyzer_name, + disable=not analyzer_report.report["malicious"], + ) + ) + + first_level_elements.append(self._vt3()) + + first_level_elements.append(self._urlhaus()) + + first_level_elements.append(self._threatfox()) + + first_level_elements.append(self._tranco()) + + second_level_elements.append(self._phishtank()) + + second_level_elements.append(self._phishing_army()) + + second_level_elements.append(self._inquest_repdb()) + + second_level_elements.append(self._otxquery()) + + page = self.Page(name="Reputation") + page.add_level( + self.Level( + position=1, + size=self.LevelSize.S_3, + horizontal_list=self.HList(value=first_level_elements), + ) + ) + page.add_level( + self.Level( + position=2, + size=self.LevelSize.S_5, + horizontal_list=self.HList(value=second_level_elements), + ) + ) + page.add_level( + self.Level( + position=3, + size=self.LevelSize.S_6, + horizontal_list=self.HList(value=third_level_elements), + ) + ) + logger.debug(f"levels: {page.to_dict()}") + return [page.to_dict()] + + @classmethod + def _monkeypatch(cls): + patches = [] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/ip_reputation_services.py b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/ip_reputation_services.py new file mode 100644 index 0000000..32f5ee3 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/ip_reputation_services.py @@ -0,0 +1,442 @@ +from logging import getLogger +from typing import Dict, List + +from api_app.analyzers_manager.models import AnalyzerReport +from api_app.choices import ReportStatus +from api_app.visualizers_manager.classes import Visualizer +from api_app.visualizers_manager.decorators import ( + visualizable_error_handler_with_params, +) +from api_app.visualizers_manager.enums import ( + VisualizableColor, + VisualizableIcon, + VisualizableSize, +) + +logger = getLogger(__name__) + + +class IPReputationServices(Visualizer): + @visualizable_error_handler_with_params("VirusTotal") + def _vt3(self): + try: + analyzer_report = self.analyzer_reports().get( + config__name="VirusTotal_v3_Get_Observable" + ) + except AnalyzerReport.DoesNotExist: + logger.warning("VirusTotal_v3_Get_Observable report does not exist") + else: + hits = ( + analyzer_report.report.get("data", {}) + .get("attributes", {}) + .get("last_analysis_stats", {}) + .get("malicious", 0) + ) + virustotal_report = self.Title( + self.Base( + value="VirusTotal", + link=analyzer_report.report["link"], + icon=VisualizableIcon.VIRUSTotal, + ), + self.Base(value=f"Engine Hits: {hits}"), + disable=analyzer_report.status != ReportStatus.SUCCESS or not hits, + ) + return virustotal_report + + @visualizable_error_handler_with_params("Greynoise") + def _greynoise(self): + try: + analyzer_report = self.analyzer_reports().get( + config__name="GreyNoiseCommunity" + ) + except AnalyzerReport.DoesNotExist: + logger.warning("GreynoiseCommunity report does not exist") + else: + message = analyzer_report.report.get("message", None) + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS or message != "Success" + ) + classification = analyzer_report.report.get("classification", "") + if classification == "benign": + icon = VisualizableIcon.LIKE + color = VisualizableColor.SUCCESS + elif classification == "malicious": + icon = VisualizableIcon.MALWARE + color = VisualizableColor.DANGER + else: # should be "unknown" + icon = VisualizableIcon.WARNING + color = VisualizableColor.INFO + greynoise_report = self.Title( + self.Base( + value="Greynoise", + link=analyzer_report.report.get("link", ""), + icon=icon, + ), + self.Base(value=analyzer_report.report.get("name", ""), color=color), + disable=disabled, + ) + return greynoise_report + + @visualizable_error_handler_with_params("URLhaus") + def _urlhaus(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="URLhaus") + except AnalyzerReport.DoesNotExist: + logger.warning("URLhaus report does not exist") + else: + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or analyzer_report.report.get("query_status", None) != "ok" + ) + urlhaus_report = self.Title( + self.Base( + value="URLhaus", + link=analyzer_report.report.get("urlhaus_reference", ""), + icon=VisualizableIcon.URLHAUS, + ), + self.Base( + value="" + if disabled + else f'found {analyzer_report.report.get("urlhaus_status", "")}' + ), + disable=disabled, + ) + return urlhaus_report + + @visualizable_error_handler_with_params("ThreatFox") + def _threatfox(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="ThreatFox") + except AnalyzerReport.DoesNotExist: + logger.warning("Threatfox report does not exist") + else: + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or analyzer_report.report.get("query_status", None) != "ok" + ) + data = analyzer_report.report.get("data", []) + malware_printable = "" + if data and isinstance(data, list): + malware_printable = data[0].get("malware_printable", "") + threatfox_report = self.Title( + self.Base( + value="ThreatFox", link=analyzer_report.report.get("link", "") + ), + self.Base(value="" if disabled else f"found {malware_printable}"), + disable=disabled, + ) + return threatfox_report + + @visualizable_error_handler_with_params("InQuest") + def _inquest_repdb(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="InQuest_REPdb") + except AnalyzerReport.DoesNotExist: + logger.warning("InQuest_REPdb report does not exist") + else: + success = analyzer_report.report.get("success", False) + data = analyzer_report.report.get("data", []) + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or not success + or not data + ) + inquest_report = self.Title( + self.Base( + value="InQuest", + link=analyzer_report.report.get("link", ""), + icon=VisualizableIcon.WARNING, + ), + self.Base(value="" if disabled else "found"), + disable=disabled, + ) + return inquest_report + + @visualizable_error_handler_with_params("AbuseIPDB Categories") + def _abuse_ipdb(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="AbuseIPDB") + except AnalyzerReport.DoesNotExist: + logger.warning("AbuseIPDB report does not exist") + return None, None + else: + data = analyzer_report.report.get("data", []) + isp = data.get("isp", "") + usage = data.get("usageType", "") + disabled = analyzer_report.status != ReportStatus.SUCCESS or ( + not isp and not usage + ) + abuse_report = self.Title( + self.Base( + value="AbuseIPDB Meta", + link=analyzer_report.report.get("permalink", ""), + icon=VisualizableIcon.INFO, + ), + self.Base(value="" if disabled else f"{isp} ({usage})"), + disable=disabled, + ) + + categories_extracted = [] + for c in data.get("reports", []): + categories_extracted.extend(c.get("categories_human_readable", [])) + categories_extracted = list(set(categories_extracted)) + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS + or not categories_extracted + ) + abuse_categories_report = self.VList( + name=self.Base( + value="AbuseIPDB Categories", + icon=VisualizableIcon.ALARM, + color=VisualizableColor.DANGER, + disable=disabled, + ), + value=[self.Base(c, disable=disabled) for c in categories_extracted], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + size=VisualizableSize.S_2, + ) + + return abuse_report, abuse_categories_report + + @visualizable_error_handler_with_params("GreedyBear Honeypots") + def _greedybear(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="GreedyBear") + except AnalyzerReport.DoesNotExist: + logger.warning("GreedyBear report does not exist") + else: + found = analyzer_report.report.get("found", False) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not found + ioc = analyzer_report.report.get("ioc", {}) + honeypots = [] + if ioc: + honeypots = list(ioc.get("general_honeypot", [])) + if ioc.get("cowrie"): + honeypots.append("Cowrie") + if ioc.get("log4j"): + honeypots.append("Log4Pot") + gb_report = self.VList( + name=self.Base( + value="GreedyBear Honeypots", + icon=VisualizableIcon.WARNING, + color=VisualizableColor.DANGER, + disable=disabled, + ), + value=[self.Base(h, disable=disabled) for h in honeypots], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + size=VisualizableSize.S_2, + ) + return gb_report + + @visualizable_error_handler_with_params( + "Crowdsec Classifications", "Crowdsec Behaviors" + ) + def _crowdsec(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="Crowdsec") + except AnalyzerReport.DoesNotExist: + logger.warning("Crowdsec report does not exist") + return None, None + else: + classifications = analyzer_report.report.get("classifications", {}) + sub_classifications = classifications.get("classifications", []) + false_positives = classifications.get("false_positives", []) + all_class = sub_classifications + false_positives + disabled = analyzer_report.status != ReportStatus.SUCCESS or not all_class + crowdsec_classification_report = self.VList( + name=self.Base( + value="Crowdsec Classifications", + icon=VisualizableIcon.INFO, + color=VisualizableColor.INFO, + disable=disabled, + ), + value=[ + self.Base(c.get("label", ""), disable=disabled) for c in all_class + ], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + size=VisualizableSize.S_2, + ) + + behaviors = analyzer_report.report.get("behaviors", []) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not behaviors + crowdsec_behaviors_report = self.VList( + name=self.Base( + value="Crowdsec Behaviors", + icon=VisualizableIcon.ALARM, + color=VisualizableColor.DANGER, + disable=disabled, + ), + value=[ + self.Base(b.get("label", ""), disable=disabled) for b in behaviors + ], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + size=VisualizableSize.S_2, + ) + return crowdsec_classification_report, crowdsec_behaviors_report + + @visualizable_error_handler_with_params("OTX Alienvault") + def _otxquery(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="OTXQuery") + except AnalyzerReport.DoesNotExist: + logger.warning("OTXQuery report does not exist") + else: + pulses = analyzer_report.report.get("pulses", []) + disabled = analyzer_report.status != ReportStatus.SUCCESS or not pulses + otx_report = self.VList( + name=self.Base( + value="OTX Alienvault", + icon=VisualizableIcon.OTX, + color=VisualizableColor.DANGER, + disable=disabled, + ), + value=[ + self.Base( + value=p.get("name", ""), + link=p.get("link", ""), + disable=disabled, + ) + for p in pulses + ], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + size=VisualizableSize.S_4, + ) + return otx_report + + @visualizable_error_handler_with_params("FireHol") + def _firehol(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="FireHol_IPList") + except AnalyzerReport.DoesNotExist: + logger.warning("FireHol_IPList report does not exist") + else: + found_in_lists = [] + for report, found in analyzer_report.report.items(): + if found: + found_in_lists.append(report) + disabled = ( + analyzer_report.status != ReportStatus.SUCCESS or not found_in_lists + ) + otx_report = self.VList( + name=self.Base( + value="FireHol", icon=VisualizableIcon.FIRE, disable=disabled + ), + value=[self.Base(f, disable=disabled) for f in found_in_lists], + start_open=True, + max_elements_number=5, + report=analyzer_report, + disable=disabled, + ) + return otx_report + + @visualizable_error_handler_with_params("Tor Exit Node") + def _tor(self): + try: + analyzer_report = self.analyzer_reports().get(config__name="TorProject") + except AnalyzerReport.DoesNotExist: + logger.warning("TorProject report does not exist") + else: + found = analyzer_report.report.get("found", False) + tor_report = self.Bool( + value="Tor Exit Node", + disable=not (analyzer_report.status == ReportStatus.SUCCESS and found), + ) + return tor_report + + @visualizable_error_handler_with_params("Talos Reputation") + def _talos(self): + try: + analyzer_report = self.analyzer_reports().get( + config__name="TalosReputation" + ) + except AnalyzerReport.DoesNotExist: + logger.warning("TalosReputation report does not exist") + else: + found = analyzer_report.report.get("found", False) + talos_report = self.Bool( + value="Talos Reputation", + disable=not (analyzer_report.status == ReportStatus.SUCCESS and found), + ) + return talos_report + + def run(self) -> List[Dict]: + first_level_elements = [] + second_level_elements = [] + third_level_elements = [] + + first_level_elements.append(self._vt3()) + + first_level_elements.append(self._greynoise()) + + first_level_elements.append(self._urlhaus()) + + first_level_elements.append(self._threatfox()) + + first_level_elements.append(self._inquest_repdb()) + + abuse_report, abuse_categories_report = self._abuse_ipdb() + third_level_elements.append(abuse_report) + + gb_report = self._greedybear() + + crowdsec_classification_report, crowdsec_behaviors_report = self._crowdsec() + second_level_elements.append(crowdsec_classification_report) + + second_level_elements.append(gb_report) + + second_level_elements.append(abuse_categories_report) + + second_level_elements.append(crowdsec_behaviors_report) + + second_level_elements.append(self._otxquery()) + + third_level_elements.append(self._firehol()) + + third_level_elements.append(self._tor()) + + third_level_elements.append(self._talos()) + + page = self.Page(name="Reputation") + page.add_level( + self.Level( + position=1, + size=self.LevelSize.S_3, + horizontal_list=self.HList(value=first_level_elements), + ) + ) + page.add_level( + self.Level( + position=2, + size=self.LevelSize.S_5, + horizontal_list=self.HList(value=second_level_elements), + ) + ) + page.add_level( + self.Level( + position=3, + size=self.LevelSize.S_6, + horizontal_list=self.HList(value=third_level_elements), + ) + ) + logger.debug(f"levels: {page.to_dict()}") + return [page.to_dict()] + + @classmethod + def _monkeypatch(cls): + patches = [] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/pivot.py b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/pivot.py new file mode 100644 index 0000000..9dfaf65 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/pivot.py @@ -0,0 +1,119 @@ +from logging import getLogger +from typing import Dict, List + +from api_app.models import Job +from api_app.pivots_manager.models import PivotConfig +from api_app.visualizers_manager.classes import Visualizer + +logger = getLogger(__name__) + + +class Pivot(Visualizer): + def run(self) -> List[Dict]: + page = self.Page("Job Pivots") + + children_element_list = [] + for pivot_report in self._job.pivotreports.all(): + pivot_report_report = pivot_report.report + children_element = [] + logger.debug(f"{pivot_report_report=}") + logger.debug(f"{type(pivot_report.config)=}") + if pivot_report_report.get("create_job", False): + children_element.extend( + [ + self._create_job_ui_element( + job=Job.objects.get(id=pivot_report_report["jobs_id"][0]), + pivot_config=pivot_report.config, + ) + ] + ) + else: + motivation = pivot_report_report.get("motivation", "") + if motivation: + motivation = f"({motivation})" + children_element.extend( + [ + self.Base( + value="", + icon=self.Icon.WARNING, + color=self.Color.WARNING, + disable=False, + ), + self.Base( + value=( + f"{pivot_report.config.name}: " + f"Job was not created {motivation}" + ), + description=pivot_report.config.description, + disable=False, + ), + ] + ) + + children_element_list.append( + self.HList( + value=children_element, + alignment=self.Alignment.START, + ) + ) + + page.add_level( + self.Level( + position=1, + size=self.LevelSize.S_3, + horizontal_list=self.HList( + value=[ + self.Title( + title=self.Base( + value="Parent job", + description=( + "This element indicates the job that created this " + "job via pivots. In case it's empty it means " + "this job has NOT been created from another." + ), + ), + value=( + self._create_job_ui_element(self._job.parent_job) + if self._job.parent_job + else self.Base(value="") + ), + size=self.Size.S_6, + disable=not bool(self._job.parent_job), + ), + self.VList( + name=self.Base( + value="Children Jobs", + description=( + "This is the list of jobs created from this job " + "via pivots." + ), + disable=False, + ), + value=children_element_list, + size=self.Size.S_6, + start_open=True, + disable=not bool(children_element_list), + ), + ], + ), + ) + ) + + return [page.to_dict()] + + def _create_job_ui_element( + self, job: Job, pivot_config: PivotConfig = None + ) -> Visualizer.Base: + label = "" + if pivot_config: + label += f"{pivot_config.name}: " + label += ( + f"Job #{job.pk} ({job.analyzed_object_name}, " + f"playbook: {job.playbook_to_execute})" + ) + return self.Base( + value=label, + link=job.url, + description=pivot_config.description if pivot_config else "", + disable=False, + ) diff --git a/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/yara.py b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/yara.py new file mode 100644 index 0000000..89246b8 --- /dev/null +++ b/Submodules/IntelOwl/api_app/visualizers_manager/visualizers/yara.py @@ -0,0 +1,117 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from logging import getLogger +from typing import Dict, List + +from api_app.analyzers_manager.models import AnalyzerConfig, AnalyzerReport +from api_app.models import Job +from api_app.visualizers_manager.classes import Visualizer +from api_app.visualizers_manager.decorators import ( + visualizable_error_handler_with_params, +) + +logger = getLogger(__name__) + + +class Yara(Visualizer): + @classmethod + def update(cls) -> bool: + pass + + @visualizable_error_handler_with_params("Analyzer") + def _yara_analyzer(self): + return self.Title( + self.Base( + value="Analyzer", + color=self.Color.DARK, + ), + self.Base(value=self.__class__.__name__), + disable=False, + ) + + @visualizable_error_handler_with_params("N# Matches") + def _yara_match_number(self, yara_num_matches: int): + return self.Title( + self.Base(value="N# Matches", color=self.Color.DARK), + self.Base(value=yara_num_matches), + disable=not yara_num_matches, + ) + + @visualizable_error_handler_with_params("VirusTotal") + def _yara_signatures(self, signatures: List[str]): + disable_signatures = not signatures + return self.VList( + name=self.Base(value="Signatures", disable=disable_signatures), + value=[ + self.Base(value=value, disable=disable_signatures) + for value in signatures + ], + disable=disable_signatures, + ) + + def run(self) -> List[Dict]: + yara_report = self.analyzer_reports().get(config__name="Yara") + yara_num_matches = sum(len(matches) for matches in yara_report.report.values()) + signatures = [ + match["match"] + for matches in yara_report.report.values() + for match in matches + if match.get("match", None) + ] + page1 = self.Page(name="Yara first page") + h1 = self.HList(value=[self._yara_analyzer()]) + page1.add_level( + self.Level(position=1, size=self.LevelSize.S_3, horizontal_list=h1) + ) + h2 = self.HList( + value=[ + self._yara_match_number(yara_num_matches), + self._yara_signatures(signatures), + ] + ) + page1.add_level( + self.Level(position=2, size=self.LevelSize.S_5, horizontal_list=h2) + ) + logger.debug(page1) + return [page1.to_dict()] + + @classmethod + def _monkeypatch(cls): + from kombu import uuid + + if not AnalyzerReport.objects.filter( + config=AnalyzerConfig.objects.get(name="Yara") + ).exists(): + report = AnalyzerReport( + config=AnalyzerConfig.objects.get(name="Yara"), + job=Job.objects.first(), + status=AnalyzerReport.Status.SUCCESS, + report={ + "inquest_yara-rules": [ + { + "url": "https://github.com/InQuest/yara-rules", + "meta": { + "URL": "https://github.com/InQuest/yara-rules", + "Author": "InQuest Labs", + "Description": "Discover embedded PE files," + " without relying on easily stripped/modified " + "header strings.", + }, + "path": "/opt/deploy/files_required/" + "yara/inquest_yara-rules/PE.rule", + "tags": [], + "match": "PE_File", + "strings": "[(0, '$mz', b'MZ')]", + "rule_url": "https://github.com/InQuest/" + "yara-rules/blob/master/PE.rule", + } + ] + }, + task_id=uuid(), + parameters={}, + ) + report.full_clean() + report.save() + patches = [] + return super()._monkeypatch(patches=patches) diff --git a/Submodules/IntelOwl/api_app/websocket.py b/Submodules/IntelOwl/api_app/websocket.py new file mode 100644 index 0000000..a5eaf9e --- /dev/null +++ b/Submodules/IntelOwl/api_app/websocket.py @@ -0,0 +1,120 @@ +import logging +from typing import List + +from asgiref.sync import async_to_sync +from channels.generic.websocket import JsonWebsocketConsumer +from channels.layers import get_channel_layer +from django.contrib.auth import get_user_model +from django.utils.functional import cached_property + +from api_app.choices import Status +from api_app.models import Job +from api_app.serializers.job import WsJobSerializer +from certego_saas.apps.organization.membership import Membership + +User = get_user_model() + + +logger = logging.getLogger(__name__) + + +class JobConsumer(JsonWebsocketConsumer): + class JobChannelGroups: + def __init__(self, job: Job) -> None: + self._job = job + + @cached_property + def job_group_name(self) -> str: + return f"job-{self._job.id}" + + @cached_property + def job_group_perm_name(self) -> str: + return f"{self.job_group_name}-perm" + + @cached_property + def group_list(self) -> List[str]: + return [self.job_group_name, self.job_group_perm_name] + + def get_group_for_user(self, user: User) -> str: + try: + is_member = self._job.user.membership.organization.user_has_membership( + user + ) + except Membership.DoesNotExist: + is_member = False + return ( + self.job_group_perm_name + if self._job.user == user or is_member + else self.job_group_name + ) + + def connect(self) -> None: + logger.debug(f"{self.scope=}") + user: User = self.scope["user"] + job_id = self.scope["url_route"]["kwargs"]["job_id"] + logger.info(f"user: {user} requested the analysis for the job {job_id}") + try: + job = Job.objects.get(id=job_id) + except Job.DoesNotExist: + logger.error(f"user: {user} request the non-existing job: {job_id}") + self.close(code=4040) + else: + self.accept() + subscribed_group = self.JobChannelGroups(job).get_group_for_user(user) + async_to_sync(self.channel_layer.group_add)( + subscribed_group, + self.channel_name, + ) + logger.debug(f"user: {user} added to the group: {subscribed_group}") + JobConsumer.serialize_and_send_job(job) + + def disconnect(self, close_code) -> None: + user: User = self.scope["user"] + job_id = self.scope["url_route"]["kwargs"]["job_id"] + try: + job = Job.objects.get(id=job_id) + except Job.DoesNotExist: + logger.warning( + f"close ws by the user: {user} for a non-existing job " + "This happens in case used tried to open a conn to a non existing job" + ) + subscribed_group = "" + else: + subscribed_group = self.JobChannelGroups(job).get_group_for_user(user) + async_to_sync(self.channel_layer.group_discard)( + subscribed_group, + self.channel_name, + ) + logger.debug( + f"user: {user} disconnected from the group: {subscribed_group}. " + f"Close code: {close_code}" + ) + self.close(code=close_code) + + def send_job(self, event) -> None: + job_data = event["job"] + logger.debug(f"job data: {job_data}") + self.send_json(content=job_data) + if job_data["status"] in Status.final_statuses(): + logger.debug("job sent to the client and terminated, close ws") + self.close() + + @classmethod + def serialize_and_send_job(cls, job: Job) -> None: + # send data + groups = cls.JobChannelGroups(job) + groups_list = groups.group_list + channel_layer = get_channel_layer() + logger.debug( + f"send data for the job: {job.id} " f"to the groups: {groups_list}" + ) + for group in groups_list: + logger.debug(f"send data to the group: {group}") + job_serializer = WsJobSerializer( + job, context={"permissions": "perm" in group} + ) + job_data = job_serializer.data + async_to_sync(channel_layer.group_send)( + group, + {"type": "send.job", "job": job_data}, + ) diff --git a/Submodules/IntelOwl/authentication/__init__.py b/Submodules/IntelOwl/authentication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/authentication/admin.py b/Submodules/IntelOwl/authentication/admin.py new file mode 100644 index 0000000..ebd4d09 --- /dev/null +++ b/Submodules/IntelOwl/authentication/admin.py @@ -0,0 +1,134 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. +from typing import Optional + +import email_utils +from django.conf import settings +from django.contrib import admin, messages +from django.db.models import Q +from django.utils.translation import ngettext + +from certego_saas.apps.user.admin import AbstractUserAdmin +from certego_saas.apps.user.models import User + +from .models import UserProfile + +__all__ = ["UserAdminView", "UserProfileAdmin"] + + +class UserProfileInlineAdmin(admin.StackedInline): + model = UserProfile + + +# certego-saas +@admin.register(User) +class UserAdminView(AbstractUserAdmin): + inlines = (UserProfileInlineAdmin,) + list_display = ( + "username", + "email", + "first_name", + "last_name", + "is_active", + "approved", + "is_email_verified", + "is_staff", + ) + actions = ["accept_users", "decline_users"] + + @admin.display(boolean=True) + def is_email_verified(self, obj: User) -> Optional[bool]: + return obj.is_email_verified + + @admin.action(description="Decline selected users") + def decline_users(self, request, queryset): + # 1. user email may or may not be verified + # 2. we can not decline users that have been already accept + declinable = Q(is_active=False) & Q(approved=None) + users = queryset.filter(declinable).all() + for user in users: + email_utils.send_email( + from_email=settings.DEFAULT_FROM_EMAIL, + recipient_list=[user.email], + subject="IntelOwl - Your account request has been declined", + template_name="authentication/emails/account-declined", + context={ + "full_name": user.get_full_name(), + "username": user.get_username(), + "host_uri": settings.HOST_URI, + "host_name": settings.HOST_NAME, + "default_email": settings.DEFAULT_EMAIL, + }, + ) + number_declined = users.update(approved=False, is_active=False) + self.message_user( + request, + ngettext( + "%d user was declined.", + "%d users were declined.", + number_declined, + ) + % number_declined, + messages.SUCCESS, + ) + + @admin.action(description="Accept selected users") + def accept_users(self, request, queryset): + # 1. user email should be verified + # 2. we can accept previously declined users + acceptable = ( + Q(email_address__is_verified=True) + & Q(is_active=False) + & (Q(approved=False) | Q(approved=None)) + ) + users = queryset.filter(acceptable).all() + for user in users: + email_utils.send_email( + from_email=settings.DEFAULT_FROM_EMAIL, + recipient_list=[user.email], + subject="IntelOwl - Your account has been successfully activated!", + template_name="authentication/emails/account-activated", + context={ + "full_name": user.get_full_name(), + "username": user.get_username(), + "host_uri": settings.HOST_URI, + "host_name": settings.HOST_NAME, + "default_email": settings.DEFAULT_EMAIL, + }, + ) + number_updated = users.update(is_active=True, approved=True) + self.message_user( + request, + ngettext( + "%d user was successfully activated.", + "%d users were successfully activated.", + number_updated, + ) + % number_updated, + messages.SUCCESS, + ) + + +@admin.register(UserProfile) +class UserProfileAdmin(admin.ModelAdmin): + list_select_related = ("user",) + list_display = ( + "user", + "user_is_active", + "user_is_approved", + "twitter_handle", + "company_name", + "company_role", + "discover_from", + "task_priority", + "is_robot", + ) + list_filter = ["task_priority", "is_robot"] + + @admin.display(boolean=True) + def user_is_active(self, obj: UserProfile) -> bool: + return obj.user.is_active + + @admin.display(boolean=True) + def user_is_approved(self, obj: UserProfile) -> Optional[bool]: + return obj.user.approved diff --git a/Submodules/IntelOwl/authentication/apps.py b/Submodules/IntelOwl/authentication/apps.py new file mode 100644 index 0000000..7917efe --- /dev/null +++ b/Submodules/IntelOwl/authentication/apps.py @@ -0,0 +1,12 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.apps import AppConfig + + +class ApiAppAuthConfig(AppConfig): + name = "authentication" + + @staticmethod + def ready() -> None: + from . import signals # noqa diff --git a/Submodules/IntelOwl/authentication/migrations/0001_initial.py b/Submodules/IntelOwl/authentication/migrations/0001_initial.py new file mode 100644 index 0000000..2f02c7a --- /dev/null +++ b/Submodules/IntelOwl/authentication/migrations/0001_initial.py @@ -0,0 +1,85 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# Generated by Django 3.2.16 on 2023-01-11 10:43 + +import django.core.validators +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="UserProfile", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "company_name", + models.CharField( + max_length=32, + validators=[django.core.validators.MinLengthValidator(3)], + ), + ), + ( + "company_role", + models.CharField( + max_length=32, + validators=[django.core.validators.MinLengthValidator(3)], + ), + ), + ( + "twitter_handle", + models.CharField( + blank=True, + default="", + max_length=16, + validators=[django.core.validators.MinLengthValidator(3)], + ), + ), + ( + "discover_from", + models.CharField( + choices=[ + ( + "search_engine", + "Search Engine (Google, DuckDuckGo, etc.)", + ), + ("was_recommended", "Recommended by friend or colleague"), + ("social_media", "Social media"), + ("blog_or_publication", "Blog or Publication"), + ("other", "Other"), + ], + default="other", + max_length=32, + ), + ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="user_profile", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name_plural": "User Profiles", + }, + ), + ] diff --git a/Submodules/IntelOwl/authentication/migrations/0002_migrate_from_durin.py b/Submodules/IntelOwl/authentication/migrations/0002_migrate_from_durin.py new file mode 100644 index 0000000..dc7db29 --- /dev/null +++ b/Submodules/IntelOwl/authentication/migrations/0002_migrate_from_durin.py @@ -0,0 +1,37 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.conf import settings +from django.db import migrations + + +def move_token_from_durin(apps, schema_editor): + if "durin" in settings.INSTALLED_APPS: + AuthToken = apps.get_model("durin", "AuthToken") + Client = apps.get_model("durin", "Client") + Token = apps.get_model("authtoken", "Token") + + for durin_token in AuthToken.objects.all(): + # export only CLI token (client name PyIntelOwl) + # only in case user didn't have a rest framework token + if ( + durin_token.client.name == "PyIntelOwl" + and not Token.objects.filter(user_id=durin_token.user.id).exists() + ): + Token.objects.create(key=durin_token.token, user_id=durin_token.user.pk) + + # delete durin db data + AuthToken.objects.all().delete() + Client.objects.all().delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("authentication", "0001_initial"), + ("authtoken", "0003_tokenproxy"), + ("api_app", "0061_job_depth_analysis"), + ] + + operations = [ + migrations.RunPython(move_token_from_durin, migrations.RunPython.noop), + ] diff --git a/Submodules/IntelOwl/authentication/migrations/0003_userprofile_is_robot_userprofile_task_priority_and_more.py b/Submodules/IntelOwl/authentication/migrations/0003_userprofile_is_robot_userprofile_task_priority_and_more.py new file mode 100644 index 0000000..a424fa4 --- /dev/null +++ b/Submodules/IntelOwl/authentication/migrations/0003_userprofile_is_robot_userprofile_task_priority_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.11 on 2024-04-04 08:16 + +import django.core.validators +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("authentication", "0002_migrate_from_durin"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="is_robot", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="userprofile", + name="task_priority", + field=models.IntegerField( + default=10, + validators=[ + django.core.validators.MaxValueValidator(10), + django.core.validators.MinValueValidator(1), + ], + ), + ), + migrations.AlterField( + model_name="userprofile", + name="user", + field=models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="profile", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/Submodules/IntelOwl/authentication/migrations/0004_alter_userprofile_company_name_and_more.py b/Submodules/IntelOwl/authentication/migrations/0004_alter_userprofile_company_name_and_more.py new file mode 100644 index 0000000..8730f54 --- /dev/null +++ b/Submodules/IntelOwl/authentication/migrations/0004_alter_userprofile_company_name_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.11 on 2024-04-04 08:27 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ( + "authentication", + "0003_userprofile_is_robot_userprofile_task_priority_and_more", + ), + ] + + operations = [ + migrations.AlterField( + model_name="userprofile", + name="company_name", + field=models.CharField( + max_length=32, + null=True, + validators=[django.core.validators.MinLengthValidator(3)], + ), + ), + migrations.AlterField( + model_name="userprofile", + name="company_role", + field=models.CharField( + max_length=32, + null=True, + validators=[django.core.validators.MinLengthValidator(3)], + ), + ), + ] diff --git a/Submodules/IntelOwl/authentication/migrations/0005_create_profiles.py b/Submodules/IntelOwl/authentication/migrations/0005_create_profiles.py new file mode 100644 index 0000000..83b3585 --- /dev/null +++ b/Submodules/IntelOwl/authentication/migrations/0005_create_profiles.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.11 on 2024-04-04 07:46 + +from django.conf import settings +from django.db import migrations + + +def migrate(apps, schema_editor): + User = apps.get_model(*settings.AUTH_USER_MODEL.split(".")) + Profile = apps.get_model("authentication", "UserProfile") + for user in User.objects.all(): + is_robot = user.username.endswith("Ingestor") + if not hasattr(user, "profile") or not user.profile: + profile = Profile( + user=user, task_priority=7 if is_robot else 10, is_robot=is_robot + ) + else: + profile = user.profile + profile.task_priority=7 if is_robot else 10 + profile.is_robot=is_robot + profile.save() + + +def reverse_migrate(apps, schema_editor): + Profile = apps.get_model("authentication", "UserProfile") + Profile.objects.all().delete() + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("authentication", "0004_alter_userprofile_company_name_and_more"), + ] + + operations = [migrations.RunPython(migrate, reverse_migrate)] diff --git a/Submodules/IntelOwl/authentication/migrations/0006_alter_userprofile_company_name_and_more.py b/Submodules/IntelOwl/authentication/migrations/0006_alter_userprofile_company_name_and_more.py new file mode 100644 index 0000000..f4cdf6e --- /dev/null +++ b/Submodules/IntelOwl/authentication/migrations/0006_alter_userprofile_company_name_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.11 on 2024-05-07 13:54 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0005_create_profiles'), + ] + + operations = [ + migrations.AlterField( + model_name='userprofile', + name='company_name', + field=models.CharField(blank=True, default='', max_length=32, validators=[django.core.validators.MinLengthValidator(3)]), + ), + migrations.AlterField( + model_name='userprofile', + name='company_role', + field=models.CharField(blank=True, default='', max_length=32, validators=[django.core.validators.MinLengthValidator(3)]), + ), + ] diff --git a/Submodules/IntelOwl/authentication/migrations/__init__.py b/Submodules/IntelOwl/authentication/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Submodules/IntelOwl/authentication/models.py b/Submodules/IntelOwl/authentication/models.py new file mode 100644 index 0000000..0089978 --- /dev/null +++ b/Submodules/IntelOwl/authentication/models.py @@ -0,0 +1,61 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.conf import settings +from django.core.validators import ( + MaxValueValidator, + MinLengthValidator, + MinValueValidator, +) +from django.db import models + +__all__ = [ + "UserProfile", +] + +# constants + + +class DiscoverFromChoices(models.TextChoices): + SEARCH_ENGINE = "search_engine", "Search Engine (Google, DuckDuckGo, etc.)" + WAS_RECOMMENDED = "was_recommended", "Recommended by friend or colleague" + SOCIAL_MEDIA = "social_media", "Social media" + BLOG_OR_PUBLICATION = "blog_or_publication", "Blog or Publication" + OTHER = "other", "Other" + + +# models + + +class UserProfile(models.Model): + # constants + DiscoverFromChoices = DiscoverFromChoices + + # fields + user = models.OneToOneField( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="profile", + ) + company_name = models.CharField( + max_length=32, validators=[MinLengthValidator(3)], default="", blank=True + ) + company_role = models.CharField( + max_length=32, validators=[MinLengthValidator(3)], default="", blank=True + ) + twitter_handle = models.CharField( + max_length=16, default="", blank=True, validators=[MinLengthValidator(3)] + ) + discover_from = models.CharField( + max_length=32, + choices=DiscoverFromChoices.choices, + default=DiscoverFromChoices.OTHER, + ) + task_priority = models.IntegerField( + default=10, validators=[MaxValueValidator(10), MinValueValidator(1)] + ) + is_robot = models.BooleanField(default=False) + + # meta + class Meta: + verbose_name_plural = "User Profiles" diff --git a/Submodules/IntelOwl/authentication/oauth.py b/Submodules/IntelOwl/authentication/oauth.py new file mode 100644 index 0000000..f503fbd --- /dev/null +++ b/Submodules/IntelOwl/authentication/oauth.py @@ -0,0 +1,14 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from authlib.integrations.django_client import OAuth +from django.conf import settings + +oauth = OAuth() +if "google" in settings.AUTHLIB_OAUTH_CLIENTS: + GOOGLE_CONF_URL = "https://accounts.google.com/.well-known/openid-configuration" + oauth.register( + name="google", + server_metadata_url=GOOGLE_CONF_URL, + client_kwargs={"scope": "openid email profile"}, + ) diff --git a/Submodules/IntelOwl/authentication/serializers.py b/Submodules/IntelOwl/authentication/serializers.py new file mode 100644 index 0000000..40d37a1 --- /dev/null +++ b/Submodules/IntelOwl/authentication/serializers.py @@ -0,0 +1,254 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +import re + +import rest_email_auth.serializers +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db import DatabaseError, transaction +from rest_framework import serializers as rfs +from rest_framework.authtoken.models import Token +from rest_framework.authtoken.serializers import AuthTokenSerializer +from slack_sdk.errors import SlackApiError + +from api_app.models import Job +from certego_saas.apps.user.serializers import ( + UserAccessSerializer as CertegoUserAccessSerializer, +) +from certego_saas.ext.upload import Slack +from certego_saas.models import User +from certego_saas.settings import certego_apps_settings +from intel_owl.consts import REGEX_PASSWORD + +from .models import UserProfile + +logger = logging.getLogger(__name__) + +__all__ = [ + "UserAccessSerializer", + "UserProfileSerializer", + "RegistrationSerializer", + "EmailVerificationSerializer", +] + + +class _AccessSerializer(rfs.ModelSerializer): + class Meta: + model = User + fields = ( + "total_submissions", + "month_submissions", + ) + + # User <-> Job stats + total_submissions = rfs.SerializerMethodField() + month_submissions = rfs.SerializerMethodField() + + @staticmethod + def get_total_submissions(obj: User) -> int: + return Job.user_total_submissions(obj) + + @staticmethod + def get_month_submissions(obj: User) -> int: + return Job.user_month_submissions(obj) + + +class UserAccessSerializer(CertegoUserAccessSerializer): + class Meta: + model = User + fields = ( + "user", + "access", + ) + + user = rfs.SerializerMethodField() + access = rfs.SerializerMethodField() + + def get_user(self, obj: User) -> dict: + data = super().get_user(obj) + data["is_staff"] = obj.is_staff + return data + + @staticmethod + def get_access(obj: User) -> dict: + return _AccessSerializer(instance=obj).data + + +class UserProfileSerializer(rfs.ModelSerializer): + class Meta: + model = UserProfile + exclude = ("user",) + + +class RegistrationSerializer(rest_email_auth.serializers.RegistrationSerializer): + class Meta: + model = User + fields = ( + "username", + "email", + "first_name", + "last_name", + "password", + "is_active", + "profile", + ) + extra_kwargs = { + "password": { + "style": {"input_type": "password"}, + "write_only": True, + }, + "first_name": { + "required": True, + "write_only": True, + }, + "last_name": { + "required": True, + "write_only": True, + }, + } + + profile = UserProfileSerializer(write_only=True) + is_active = rfs.BooleanField(default=False, read_only=True) + + def validate_profile(self, profile): + logger.info(f"{profile}") + + self._profile_serializer = UserProfileSerializer(data=profile) + self._profile_serializer.is_valid(raise_exception=True) + return profile + + def validate_password(self, password): + super().validate_password(password) + + if re.match(REGEX_PASSWORD, password): + return password + else: + raise ValidationError("Invalid password") + + def create(self, validated_data): + validated_data.pop("profile", None) + validated_data["is_active"] = False + user = None + try: + user = super().create(validated_data) + + # update profile object only if user object was actually saved + if getattr(user, "pk", None): + self._profile_serializer.update( + user.profile, self._profile_serializer.data + ) + user.refresh_from_db() + except DatabaseError: + transaction.rollback() + return user + + +class EmailVerificationSerializer( + rest_email_auth.serializers.EmailVerificationSerializer +): + def validate_key(self, key): + try: + return super().validate_key(key) + except rfs.ValidationError as exc: + # custom error messages + err_str = str(exc.detail) + if "invalid" in err_str: + exc.detail = ( + "The provided verification key" + " is invalid or your email address is already verified." + ) + if "expired" in err_str: + exc.detail = ( + "The provided verification key" + " has expired or your email address is already verified." + ) + raise exc + + def save(self): + """ + Confirm the email address matching the confirmation key. + Then mark user as active. + """ + user = self._confirmation.email.user + with transaction.atomic(): + super().save() + + # Send msg on slack + if ( + certego_apps_settings.SLACK_TOKEN + and certego_apps_settings.DEFAULT_SLACK_CHANNEL + ): + userprofile = user.user_profile + user_admin_link = ( + f"{settings.WEB_CLIENT_URL}/admin/certego_saas_user/user/{user.pk}" + ) + userprofile_admin_link = ( + f"{settings.WEB_CLIENT_URL}" + f"/admin/authentication/userprofile/{userprofile.pk}" + ) + slack = Slack() + try: + slack.send_message( + title="Newly registered user!!", + body=( + f"- User(#{user.pk}, {user.username}," + f"{user.email}, <{user_admin_link} |admin link>)\n" + f"- UserProfile({userprofile.company_name}," + f"{userprofile.company_role}, )" + f"<{userprofile_admin_link} |admin link>)" + ), + channel=certego_apps_settings.DEFAULT_SLACK_CHANNEL, + ) + except SlackApiError as exc: + slack.log.error( + f"Slack message failed for user(#{user.pk}) with error: {str(exc)}" + ) + + +class LoginSerializer(AuthTokenSerializer): + def validate(self, attrs): + try: + return super().validate(attrs) + except rfs.ValidationError as exc: + try: + user = User.objects.get(username=attrs["username"]) + except User.DoesNotExist: + # we do not want to leak info + # so just raise the original exception + raise exc + else: + # custom error messages + if not user.is_active: + if user.is_email_verified is False: + exc.detail = "Your account is pending email verification." + elif user.approved is None: + exc.detail = "Your account is pending activation by our team." + elif user.approved is False: + exc.detail = "Your account was declined." + logger.info( + f"User {user} is not active. Error message: {exc.detail}" + ) + # else + raise exc + + +class TokenSerializer(rfs.ModelSerializer): + class Meta: + model = Token + fields = [ + "key", + "created", + ] + read_only_fields = [ + "key", + "created", + ] + + def validate(self, data): + user = self.context["user"] + if Token.objects.filter(user=user).exists(): + raise rfs.ValidationError("An API token was already issued to you.") + data["user"] = user + return data diff --git a/Submodules/IntelOwl/authentication/signals.py b/Submodules/IntelOwl/authentication/signals.py new file mode 100644 index 0000000..c637ee9 --- /dev/null +++ b/Submodules/IntelOwl/authentication/signals.py @@ -0,0 +1,11 @@ +from django.conf import settings +from django.db.models.signals import post_save +from django.dispatch import receiver + +from authentication.models import UserProfile + + +@receiver(post_save, sender=settings.AUTH_USER_MODEL) +def post_save_user(sender, instance, created, **kwargs): + if created: + UserProfile.objects.create(user=instance) diff --git a/Submodules/IntelOwl/authentication/templates/authentication/emails/account-activated.html b/Submodules/IntelOwl/authentication/templates/authentication/emails/account-activated.html new file mode 100644 index 0000000..998b1f2 --- /dev/null +++ b/Submodules/IntelOwl/authentication/templates/authentication/emails/account-activated.html @@ -0,0 +1,18 @@ +{% extends "./base.html" %} + +{% block content %} +
+ Hello, {{ full_name }}! + +

+

You've got access! 🎉

+

+ +

+ Your account @{{ username }} has been verified by us and you + can now + login + to and use {{ host_name }}. +

+
+{% endblock %} \ No newline at end of file diff --git a/Submodules/IntelOwl/authentication/templates/authentication/emails/account-declined.html b/Submodules/IntelOwl/authentication/templates/authentication/emails/account-declined.html new file mode 100644 index 0000000..d0eb044 --- /dev/null +++ b/Submodules/IntelOwl/authentication/templates/authentication/emails/account-declined.html @@ -0,0 +1,13 @@ +{% extends "./base.html" %} {% block content %} +
+ Hello, {{ full_name }}! + +

+ We regret to inform you that your account request (@{{ username }}) on {{ host_name }} has been declined. You can sign up again with a + business email address and not a personal one to increase your chances of + getting access. +

+
+{% endblock %} diff --git a/Submodules/IntelOwl/authentication/templates/authentication/emails/base.html b/Submodules/IntelOwl/authentication/templates/authentication/emails/base.html new file mode 100644 index 0000000..1ddbbf4 --- /dev/null +++ b/Submodules/IntelOwl/authentication/templates/authentication/emails/base.html @@ -0,0 +1,27 @@ +{% load static %} + + +
+ IntelOwl: logo +
+
+ +
{% block content %} {% endblock %} +

+ Note: If you believe you received this email in error, please contact us at + {{ default_email }}. +

+
+ +
+
+ Thank you, +
+ IntelOwl © +
+ diff --git a/Submodules/IntelOwl/authentication/templates/authentication/emails/duplicate-email.html b/Submodules/IntelOwl/authentication/templates/authentication/emails/duplicate-email.html new file mode 100644 index 0000000..1735db4 --- /dev/null +++ b/Submodules/IntelOwl/authentication/templates/authentication/emails/duplicate-email.html @@ -0,0 +1,21 @@ +{% load static %} + + +
+
+ Dear IntelOwl user, + +

+ As part of our commitment to keep IntelOwl and its users secure, we notify + you that someone just attempted to register with this email address. +

+
+
+ +
+
+ Thank you, +
+ IntelOwl © +
+ \ No newline at end of file diff --git a/Submodules/IntelOwl/authentication/templates/authentication/emails/reset-password.html b/Submodules/IntelOwl/authentication/templates/authentication/emails/reset-password.html new file mode 100644 index 0000000..6ce04d1 --- /dev/null +++ b/Submodules/IntelOwl/authentication/templates/authentication/emails/reset-password.html @@ -0,0 +1,36 @@ +{% load static %} + + +
+
+ Dear IntelOwl user, + +

+ Please click the link below to reset your password on IntelOwl. +

+ + + +

or, you may also copy and paste directly into your browser's URL bar.

+ +
{{ reset_url }}
+ +

+ Note: This URL is valid only for the next 24 hours. +

+ +

+ If you did not request a password reset, you can safely ignore this email. +

+
+
+ +
+
+ Thank you, +
+ IntelOwl © +
+ diff --git a/Submodules/IntelOwl/authentication/templates/authentication/emails/verify-email.html b/Submodules/IntelOwl/authentication/templates/authentication/emails/verify-email.html new file mode 100644 index 0000000..320fea7 --- /dev/null +++ b/Submodules/IntelOwl/authentication/templates/authentication/emails/verify-email.html @@ -0,0 +1,34 @@ +{% load static %} + + +
+
+ Hello! + +

+ Please click the link below to verify your email address. +

+ + + +

or, you may also copy and paste directly into your browser's URL bar.

+ +
{{ verification_url }}
+ +

+ Note: This URL is valid only for the next 24 hours. +

+ +
+
+ +
+
+ Thank you, +
+ IntelOwl © +
+ + diff --git a/Submodules/IntelOwl/authentication/urls.py b/Submodules/IntelOwl/authentication/urls.py new file mode 100644 index 0000000..a2d269a --- /dev/null +++ b/Submodules/IntelOwl/authentication/urls.py @@ -0,0 +1,60 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +from django.urls import include, path +from rest_framework import routers + +from .views import ( + APIAccessTokenView, + ChangePasswordView, + EmailVerificationView, + GoogleLoginCallbackView, + LoginView, + LogoutView, + PasswordResetRequestView, + PasswordResetView, + RegistrationView, + ResendVerificationView, + checkConfiguration, + google_login, +) + +router = routers.DefaultRouter(trailing_slash=False) + +urlpatterns = [ + # django-rest-email-auth + path( + "verify-email", + EmailVerificationView.as_view(), + name="auth_verify-email", + ), + path( + "resend-verification", + ResendVerificationView.as_view(), + name="auth_resend-verification", + ), + path( + "register", + RegistrationView.as_view(), + name="auth_register", + ), + path( + "request-password-reset", + PasswordResetRequestView.as_view(), + name="auth_request-password-reset", + ), + path("reset-password", PasswordResetView.as_view(), name="auth_reset-password"), + # auth + path("login", LoginView.as_view(), name="auth_login"), + path("logout", LogoutView.as_view(), name="auth_logout"), + path("changepassword", ChangePasswordView.as_view(), name="auth_changepassword"), + path("apiaccess", APIAccessTokenView.as_view(), name="auth_apiaccess"), + path("google", google_login, name="oauth_google"), + path( + "google-callback", + GoogleLoginCallbackView.as_view(), + name="oauth_google_callback", + ), + path("configuration", checkConfiguration, name="auth_configuration"), + path("", include(router.urls)), +] diff --git a/Submodules/IntelOwl/authentication/views.py b/Submodules/IntelOwl/authentication/views.py new file mode 100644 index 0000000..1459bd1 --- /dev/null +++ b/Submodules/IntelOwl/authentication/views.py @@ -0,0 +1,433 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import logging +from typing import List + +import rest_email_auth.views +from authlib.integrations.base_client import OAuthError +from authlib.oauth2 import OAuth2Error +from django.conf import settings +from django.contrib.auth import get_user_model, login, logout +from django.contrib.auth.hashers import check_password +from django.shortcuts import redirect +from drf_spectacular.utils import extend_schema as add_docs +from rest_framework import status +from rest_framework.authtoken.models import Token +from rest_framework.decorators import api_view, permission_classes +from rest_framework.exceptions import AuthenticationFailed, NotFound +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.reverse import reverse +from rest_framework.views import APIView + +from certego_saas.ext.mixins import RecaptchaV2Mixin +from certego_saas.ext.throttling import POSTUserRateThrottle +from intel_owl.settings import AUTH_USER_MODEL + +from .oauth import oauth +from .serializers import ( + EmailVerificationSerializer, + LoginSerializer, + RegistrationSerializer, + TokenSerializer, +) + +logger = logging.getLogger(__name__) + +""" Auth API endpoints """ + +User: AUTH_USER_MODEL = get_user_model() + + +class PasswordResetRequestView( + rest_email_auth.views.PasswordResetRequestView, RecaptchaV2Mixin +): + """ + Handles requests for password reset. + + Args: + rest_email_auth.views.PasswordResetRequestView: + The parent view class for password reset requests. + RecaptchaV2Mixin: A mixin for reCAPTCHA verification. + """ + + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + +class PasswordResetView(rest_email_auth.views.PasswordResetView, RecaptchaV2Mixin): + """ + Handles password reset. + + Args: + rest_email_auth.views.PasswordResetView: + The parent view class for password reset. + RecaptchaV2Mixin: A mixin for reCAPTCHA verification. + """ + + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + +class EmailVerificationView(rest_email_auth.views.EmailVerificationView): + """ + Handles email verification. + + Args: + rest_email_auth.views.EmailVerificationView: + The parent view class for email verification. + """ + + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + serializer_class = EmailVerificationSerializer + + +class RegistrationView(rest_email_auth.views.RegistrationView, RecaptchaV2Mixin): + """ + Handles user registration. + + Args: + rest_email_auth.views.RegistrationView: + The parent view class for user registration. + RecaptchaV2Mixin: A mixin for reCAPTCHA verification. + """ + + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + serializer_class = RegistrationSerializer + + def get_serializer_class(self): # skipcq: PYL-R0201 + """ + Returns the serializer class for registration. + + Returns: + RegistrationSerializer: The serializer class for user registration. + """ + return RegistrationSerializer + + +class ResendVerificationView( + rest_email_auth.views.ResendVerificationView, RecaptchaV2Mixin +): + """ + Handles re-sending email verification. + + Args: + rest_email_auth.views.ResendVerificationView: + The parent view class for resending email verification. + RecaptchaV2Mixin: A mixin for reCAPTCHA verification. + """ + + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + +class LoginView(RecaptchaV2Mixin): + """ + Handles user login. + + Args: + RecaptchaV2Mixin: A mixin for reCAPTCHA verification. + """ + + authentication_classes: List = [] + permission_classes: List = [] + throttle_classes: List = [POSTUserRateThrottle] + + @staticmethod + def validate_and_return_user(request): + """ + Validates user credentials and returns the user object. + + Args: + request (Request): The request object containing user credentials. + + Returns: + Any: The authenticated user object. + """ + serializer = LoginSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + return serializer.validated_data["user"] + + def post(self, request, *args, **kwargs): + """ + Handles POST request for user login. + + Args: + request (Request): The request object containing user credentials. + + Returns: + Response: The response object. + """ + try: + self.get_serializer() # for RecaptchaV2Mixin + except AssertionError: + # it will raise this bcz `serializer_class` is not defined + pass + user = self.validate_and_return_user(request=request) + logger.info(f"perform_login received request from '{user.username}''.") + login(request, user) + return Response() + + +class ChangePasswordView(APIView): + """ + Handles changing user password. + """ + + permission_classes = [IsAuthenticated] + + @staticmethod + def post(request: Request) -> Response: + # Get the old password and new password from the request data + """ + Handles POST request for changing user password. + + Args: + request (Request): The request object containing old and new passwords. + + Returns: + Response: The response object. + """ + old_password = request.data.get("old_password") + new_password = request.data.get("new_password") + + # Check if the old password matches the user's current password + user = request.user + uname = user.username + if not check_password(old_password, user.password): + logger.info(f"'{uname}' has inputted invalid old password.") + # Return an error response if the old password doesn't match + return Response( + {"error": "Invalid old password"}, status=status.HTTP_400_BAD_REQUEST + ) + + # Set the new password for the user + user.set_password(new_password) + user.save() + + # Return a success response + return Response({"message": "Password changed successfully"}) + + +class LogoutView(APIView): + """ + Handles user logout. + """ + + permission_classes = [IsAuthenticated] + + def post(self, request, *args, **kwargs): # skipcq: PYL-R0201 + """ + Handles POST request for user logout. + + Args: + request (Request): The request object. + + Returns: + Response: The response object. + """ + user = request.user + logger.info(f"perform_logout received request from '{user.username}''.") + logout(request) + return Response() + + +@add_docs( + description="""This endpoint redirects to Google OAuth login.""", +) +@api_view(["GET"]) +@permission_classes([AllowAny]) +def google_login(request: Request): + """ + Redirect to Google OAuth login + + Args: + request (Request): The request object. + + Returns: + Response: The response object. + """ + redirect_uri = request.build_absolute_uri(reverse("oauth_google_callback")) + try: + response = oauth.google.authorize_redirect(request, redirect_uri) + if request.query_params.get("no_redirect") == "true": + return Response(status=status.HTTP_200_OK) + return response + except AttributeError as error: + if "No such client: " in str(error): + raise AuthenticationFailed("Google OAuth is not configured.") + raise error + + +class GoogleLoginCallbackView(LoginView): + """ + Handles Google OAuth login callback. + """ + + @staticmethod + def validate_and_return_user(request): + """ + Validates Google OAuth token and returns the user object. + + Args: + request (Request): The request object. + + Returns: + Any: The authenticated user object. + """ + try: + token = oauth.google.authorize_access_token(request) + except ( + OAuthError, + OAuth2Error, + ): + # Not giving out the actual error as we risk exposing the client secret + raise AuthenticationFailed("OAuth authentication error.") + user = token.get("userinfo") + user_email = user.get("email") + user_name = user.get("name") + try: + return User.objects.get(email=user_email) + except User.DoesNotExist: + logging.info("[Google Oauth] User does not exist. Creating new user.") + return User.objects.create_user( + email=user_email, username=user_name, password=None + ) + + def get(self, request, *args, **kwargs): + return self.post(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + user = self.validate_and_return_user(request=request) + logger.info(f"perform_login received request from '{user.username}''.") + login(request, user) + # Uncomment this for local testing + # return redirect("http://localhost/login") + return redirect(self.request.build_absolute_uri("/login")) + + +@api_view(["get"]) +@permission_classes([AllowAny]) +def checkConfiguration(request): + """ + Checks the configuration settings. + + Args: + request (Request): The request object. + + Returns: + Response: The response object. + """ + logger.info(f"Requested checking configuration from {request.user}.") + page = request.query_params.get("page") + register_uri = reverse("auth_register") + errors = {} + + if page == register_uri.split("/")[-1]: + # email setup + if not settings.DEFAULT_FROM_EMAIL: + errors["DEFAULT_FROM_EMAIL"] = "required" + if not settings.DEFAULT_EMAIL: + errors["DEFAULT_EMAIL"] = "required" + + # SES backend + if settings.AWS_SES: + if not settings.AWS_ACCESS_KEY_ID or not settings.AWS_SECRET_ACCESS_KEY: + errors["AWS SES backend"] = "configuration required" + else: + # SMTP backend + if not all( + [ + settings.EMAIL_HOST, + settings.EMAIL_HOST_USER, + settings.EMAIL_HOST_PASSWORD, + settings.EMAIL_PORT, + ] + ): + errors["SMTP backend"] = "configuration required" + + # if you are in production environment + if settings.USE_RECAPTCHA: + # recaptcha key + if settings.DRF_RECAPTCHA_SECRET_KEY == "fake": + errors["RECAPTCHA_SECRET_KEY"] = "required" + + logger.info(f"Configuration errors: {errors}") + return Response( + status=status.HTTP_200_OK, data={"errors": errors} if errors else {} + ) + + +class APIAccessTokenView(APIView): + """ + - ``GET`` -> get token-client pair info + - ``POST`` -> create and get token-client pair info + - ``DELETE`` -> delete existing API access token + Handles API access token operations. + """ + + permission_classes = [IsAuthenticated] + + def get_object(self): + try: + instance = Token.objects.get(user__pk=self.request.user.pk) + except Token.DoesNotExist: + raise NotFound() + + return instance + + def get(self, request, *args, **kwargs): + """ + Handles GET request for retrieving API access token. + + Args: + request (Request): The request object. + + Returns: + Response: The response object. + """ + instance = self.get_object() + logger.info(f" user {request.user} request the API token") + serializer = TokenSerializer(instance) + return Response(serializer.data) + + def post(self, request): # skipcq: PYL-R0201 + """ + Handles POST request for creating API access token. + + Args: + request (Request): The request object. + + Returns: + Response: The response object. + """ + username = request.user.username + logger.info(f"user {username} send a request to create the API token") + serializer = TokenSerializer(data={}, context={"user": request.user}) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + + def delete(self, request): + """ + Handles DELETE request for deleting API access token. + + Args: + request (Request): The request object. + + Returns: + Response: The response object. + """ + logger.info(f"user {request.user} send a request to delete the API token") + instance = self.get_object() + instance.delete() + return Response(status=status.HTTP_204_NO_CONTENT) diff --git a/Submodules/IntelOwl/configuration/Kibana-Saved-Conf.ndjson b/Submodules/IntelOwl/configuration/Kibana-Saved-Conf.ndjson new file mode 100644 index 0000000..0003b25 --- /dev/null +++ b/Submodules/IntelOwl/configuration/Kibana-Saved-Conf.ndjson @@ -0,0 +1,12 @@ +{"attributes":{"fields":"[{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"analyzer_reports.end_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.errors\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.abuseConfidenceScore\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.countryCode\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.countryCode.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.countryCode\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.countryName\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.countryName.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.countryName\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.domain\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.domain.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.domain\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.hostnames\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.hostnames.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.hostnames\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.ipAddress\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.ipAddress.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.ipAddress\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.ipVersion\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.isPublic\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.isWhitelisted\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.isp\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.isp.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.isp\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.lastReportedAt\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.numDistinctUsers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.categories\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.categories_human_readable\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.categories_human_readable.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.reports.categories_human_readable\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.comment\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.comment.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.reports.comment\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.reportedAt\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.reporterCountryCode\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.reporterCountryCode.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.reports.reporterCountryCode\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.reporterCountryName\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.reporterCountryName.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.reports.reporterCountryName\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.reports.reporterId\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.totalReports\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.usageType\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.data.usageType.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.data.usageType\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.accuracy_radius\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.area_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.asn\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.asn.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.asn\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.charset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.city\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.city.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.city\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.city_data\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.continent_code\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.continent_code.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.continent_code\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_code\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_code.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.country_code\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_code2\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_code2.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.country_code2\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_code3\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_code3.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.country_code3\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.country_name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.country_name\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.dma_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.flag_title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.flag_title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.flag_title\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.flag_url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.flag_url.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.flag_url\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.latitude\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.longitude\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.postal_code\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.postal_code.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.postal_code\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.region\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.region.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.region\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.subdivision\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.geo.subdivision.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.geo.subdivision\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.address\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.address.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.address\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.asn\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.asn.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.asn\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.asset_type\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.asset_type.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.asset_type\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.first\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.flag_title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.flag_title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.flag_title\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.flag_url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.flag_url.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.flag_url\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.hostname\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.hostname.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.hostname\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.indicator_link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.indicator_link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.indicator_link\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.last\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.record_type\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.passive_dns.record_type.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.passive_dns.record_type\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.permalink\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.permalink.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.permalink\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.query_status\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.query_status.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.query_status\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.domain\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.domain.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.url_list.domain\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.encoded\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.encoded.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.url_list.encoded\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.hostname\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.hostname.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.url_list.hostname\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.httpcode\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.result.urlworker.http_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.result.urlworker.ip\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.result.urlworker.ip.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.url_list.result.urlworker.ip\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.report.url_list.url.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"analyzer_reports.report.url_list.url\"},\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzer_reports.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"analyzer_reports\"}}},{\"name\":\"analyzers_to_execute\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"connector_reports.end_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"connector_reports\"}}},{\"name\":\"connector_reports.errors\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"connector_reports\"}}},{\"name\":\"connector_reports.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"connector_reports\"}}},{\"name\":\"connector_reports.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"connector_reports\"}}},{\"name\":\"connector_reports.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"connector_reports\"}}},{\"name\":\"connectors_to_execute\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"errors\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"file_mimetype\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"finished_analysis_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"is_sample\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observable_classification\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observable_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"received_request_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags.color\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"tags\"}}},{\"name\":\"tags.label\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"tags\"}}},{\"name\":\"tlp\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]","title":"jobs*"},"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","migrationVersion":{"index-pattern":"7.6.0"},"references":[],"type":"index-pattern","updated_at":"2021-08-31T15:01:53.922Z","version":"WzUzMSwzXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Jobs by status","uiStateJSON":"{}","version":1,"visState":"{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"status\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"title\":\"Jobs by status\"}"},"id":"666fae30-dcc3-11ea-a089-f78cfacc9060","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:37:33.592Z","version":"WzM2LDNd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Jobs by is_sample","uiStateJSON":"{}","version":1,"visState":"{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"is_sample\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"file\"}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"title\":\"Jobs by is_sample\"}"},"id":"c1795830-dcc3-11ea-a089-f78cfacc9060","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:37:33.592Z","version":"WzM3LDNd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":" Jobs by Observable Type","uiStateJSON":"{}","version":1,"visState":"{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"observable_classification\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"title\":\" Jobs by Observable Type\"}"},"id":"27c58be0-dcc4-11ea-a089-f78cfacc9060","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:37:33.592Z","version":"WzM4LDNd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":" Jobs by File mimetype","uiStateJSON":"{}","version":1,"visState":"{\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"file_mimetype\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"}}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}},\"title\":\" Jobs by File mimetype\"}"},"id":"5a367300-dcc4-11ea-a089-f78cfacc9060","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:37:33.592Z","version":"WzM5LDNd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":" Jobs by TLP","uiStateJSON":"{}","version":1,"visState":"{\"title\":\" Jobs by TLP\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"tlp\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"isDonut\":true,\"labels\":{\"show\":true,\"values\":true,\"last_level\":true,\"truncate\":100}}}"},"id":"e223e5d0-0a69-11ec-891d-858ff4342000","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:51:17.153Z","version":"WzM2NSwzXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Observables Table","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Observables Table\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"observable_name\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"observable_classification\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"},\"schema\":\"bucket\"},{\"id\":\"5\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"md5\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10000000,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"bucket\"},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"status\",\"orderBy\":\"_key\",\"order\":\"desc\",\"size\":10,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"bucket\"},{\"id\":\"6\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"analyzers_to_execute\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10000,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\",\"row\":true}}"},"id":"14ef8be0-dcc6-11ea-a089-f78cfacc9060","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:41:46.551Z","version":"WzE1NSwzXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Files Table","uiStateJSON":"{\"vis\":{\"params\":{\"sort\":{\"columnIndex\":null,\"direction\":null}}}}","version":1,"visState":"{\"title\":\"Files Table\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"file_name\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"},\"schema\":\"bucket\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"file_mimetype\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"exclude\":\"\\\"\\\"\"},\"schema\":\"bucket\"},{\"id\":\"5\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"md5\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10000000,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"bucket\"},{\"id\":\"4\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"status\",\"orderBy\":\"_key\",\"order\":\"desc\",\"size\":10,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"bucket\"},{\"id\":\"6\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"analyzers_to_execute\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10000,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"bucket\"},{\"id\":\"7\",\"enabled\":true,\"type\":\"date_range\",\"params\":{\"field\":\"received_request_time\",\"ranges\":[{\"from\":\"now-1w\",\"to\":\"now\"}]},\"schema\":\"split\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\",\"row\":true}}"},"id":"3d17b2a0-dcc6-11ea-a089-f78cfacc9060","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:42:10.342Z","version":"WzE2OSwzXQ=="} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"},"optionsJSON":"{\"hidePanelTitles\":false,\"useMargins\":true}","panelsJSON":"[{\"version\":\"7.9.0\",\"gridData\":{\"x\":0,\"y\":0,\"w\":9,\"h\":9,\"i\":\"f2a0b939-c35e-4733-a90e-4a0821aa5a00\"},\"panelIndex\":\"f2a0b939-c35e-4733-a90e-4a0821aa5a00\",\"embeddableConfig\":{},\"panelRefName\":\"panel_0\"},{\"version\":\"7.9.0\",\"gridData\":{\"x\":9,\"y\":0,\"w\":9,\"h\":9,\"i\":\"1dc42bdb-cf63-48d0-8073-b541eb70b582\"},\"panelIndex\":\"1dc42bdb-cf63-48d0-8073-b541eb70b582\",\"embeddableConfig\":{\"legendOpen\":false,\"vis\":{\"legendOpen\":true}},\"panelRefName\":\"panel_1\"},{\"version\":\"7.9.0\",\"gridData\":{\"x\":18,\"y\":0,\"w\":12,\"h\":9,\"i\":\"3e83b679-a814-4541-aa2f-97f568479428\"},\"panelIndex\":\"3e83b679-a814-4541-aa2f-97f568479428\",\"embeddableConfig\":{},\"panelRefName\":\"panel_2\"},{\"version\":\"7.9.0\",\"gridData\":{\"x\":30,\"y\":0,\"w\":10,\"h\":9,\"i\":\"1bd4a866-32c9-4103-b9c2-5ad908248a4a\"},\"panelIndex\":\"1bd4a866-32c9-4103-b9c2-5ad908248a4a\",\"embeddableConfig\":{},\"panelRefName\":\"panel_3\"},{\"version\":\"7.9.0\",\"gridData\":{\"x\":40,\"y\":0,\"w\":8,\"h\":9,\"i\":\"9bb7f26c-251f-4fa7-a066-ee4d555bbf19\"},\"panelIndex\":\"9bb7f26c-251f-4fa7-a066-ee4d555bbf19\",\"embeddableConfig\":{},\"panelRefName\":\"panel_4\"},{\"version\":\"7.9.0\",\"gridData\":{\"x\":0,\"y\":9,\"w\":25,\"h\":16,\"i\":\"42f5d88f-b8cd-4e25-83ac-b215c3d2af47\"},\"panelIndex\":\"42f5d88f-b8cd-4e25-83ac-b215c3d2af47\",\"embeddableConfig\":{\"params\":{\"sort\":{\"columnIndex\":2,\"direction\":\"asc\"}},\"vis\":{\"params\":{\"sort\":{\"columnIndex\":1,\"direction\":\"asc\"}}}},\"panelRefName\":\"panel_5\"},{\"version\":\"7.9.0\",\"gridData\":{\"x\":25,\"y\":9,\"w\":23,\"h\":16,\"i\":\"7de90d53-92f6-478a-b2d8-2350376c953a\"},\"panelIndex\":\"7de90d53-92f6-478a-b2d8-2350376c953a\",\"embeddableConfig\":{},\"panelRefName\":\"panel_6\"}]","timeRestore":false,"title":"Intel Owl First Dashboard","version":1},"id":"418637b0-dcc3-11ea-a089-f78cfacc9060","migrationVersion":{"dashboard":"7.3.0"},"references":[{"id":"666fae30-dcc3-11ea-a089-f78cfacc9060","name":"panel_0","type":"visualization"},{"id":"c1795830-dcc3-11ea-a089-f78cfacc9060","name":"panel_1","type":"visualization"},{"id":"27c58be0-dcc4-11ea-a089-f78cfacc9060","name":"panel_2","type":"visualization"},{"id":"5a367300-dcc4-11ea-a089-f78cfacc9060","name":"panel_3","type":"visualization"},{"id":"e223e5d0-0a69-11ec-891d-858ff4342000","name":"panel_4","type":"visualization"},{"id":"14ef8be0-dcc6-11ea-a089-f78cfacc9060","name":"panel_5","type":"visualization"},{"id":"3d17b2a0-dcc6-11ea-a089-f78cfacc9060","name":"panel_6","type":"visualization"}],"type":"dashboard","updated_at":"2021-08-31T14:51:05.889Z","version":"WzM1NSwzXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Connectors to execute","uiStateJSON":"{\"vis\":{\"colors\":{\"Count\":\"#806EB7\"}}}","version":1,"visState":"{\"title\":\"Connectors to execute\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"connectors_to_execute\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}"},"id":"cfb5f670-0a6b-11ec-891d-858ff4342000","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:57:50.422Z","version":"WzQ0NywzXQ=="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Analyzers to execute","uiStateJSON":"{\"vis\":{\"colors\":{\"Count\":\"#806EB7\"}}}","version":1,"visState":"{\"title\":\"Analyzers to execute\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"analyzers_to_execute\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":20,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"}}}"},"id":"c4863170-0a6b-11ec-891d-858ff4342000","migrationVersion":{"visualization":"7.8.0"},"references":[{"id":"10336ac0-dcc3-11ea-a089-f78cfacc9060","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-08-31T14:57:31.655Z","version":"WzQ0NCwzXQ=="} +{"exportedCount":11,"missingRefCount":0,"missingReferences":[]} \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/cyberchef_recipes.json b/Submodules/IntelOwl/configuration/cyberchef_recipes.json new file mode 100644 index 0000000..d90947b --- /dev/null +++ b/Submodules/IntelOwl/configuration/cyberchef_recipes.json @@ -0,0 +1,10 @@ +{ + "to decimal": [ + { + "op": "To Decimal", + "args": [ + "Space", false + ] + } + ] +} diff --git a/Submodules/IntelOwl/configuration/elastic_search_mappings/intel_owl_bi.json b/Submodules/IntelOwl/configuration/elastic_search_mappings/intel_owl_bi.json new file mode 100644 index 0000000..0342ef4 --- /dev/null +++ b/Submodules/IntelOwl/configuration/elastic_search_mappings/intel_owl_bi.json @@ -0,0 +1,46 @@ +{ + "settings" : { + "number_of_shards" : 1, + "number_of_replicas": 0 + }, + "mappings": { + "dynamic": false, + "properties": { + "timestamp": { + "type": "date" + }, + "application": { + "type": "keyword" + }, + "username": { + "type": "keyword" + }, + "environment": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "process_time": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "end_time": { + "type": "date" + }, + "parameters": { + "type": "object", + "dynamic": true + }, + "playbook": { + "type": "keyword" + }, + "class_instance": { + "type": "keyword" + } + + } + } +} \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/intel_owl.ini b/Submodules/IntelOwl/configuration/intel_owl.ini new file mode 100644 index 0000000..b0bed5c --- /dev/null +++ b/Submodules/IntelOwl/configuration/intel_owl.ini @@ -0,0 +1,29 @@ +[uwsgi] +project = intel_owl +base = /opt/deploy/intel_owl + +chdir = %(base) +module = %(project).wsgi:application + +master = true +processes = 12 +enable-threads = true + +socket = 0.0.0.0:8001 +chown = www-data:www-data +vacuum = true +single-interpreter = true +harakiri = 300 + +logto = /var/log/intel_owl/uwsgi/intel_owl.log +uid = www-data +gid = www-data + +max-requests = 1000 +max-worker-lifetime = 3600 +reload-on-rss = 2048 +worker-reload-mercy = 3600 + +buffer-size = 32768 + +need-app = true \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/ldap_config.py b/Submodules/IntelOwl/configuration/ldap_config.py new file mode 100644 index 0000000..09a260b --- /dev/null +++ b/Submodules/IntelOwl/configuration/ldap_config.py @@ -0,0 +1,59 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# Check the documentation for the details on how to configure LDAP +# https://intelowl.readthedocs.io/en/latest/Advanced-Configuration.html#ldap + +import ldap +from django_auth_ldap.config import GroupOfNamesType, LDAPSearch + +# Baseline configuration. +AUTH_LDAP_SERVER_URI = "ldap://ldap.example.com" + +AUTH_LDAP_BIND_DN = "cn=django-agent,dc=example,dc=com" +AUTH_LDAP_BIND_PASSWORD = "" +AUTH_LDAP_USER_SEARCH = LDAPSearch( + "ou=users,dc=example,dc=com", + ldap.SCOPE_SUBTREE, + "(uid=%(user)s)", + # for Windows Active Directory it could make sense to change the previous line with the next one + # "(sAMAccountName=%(user)s)", + # see also: https://github.com/intelowlproject/IntelOwl/issues/1433 +) +# Or: +# AUTH_LDAP_USER_DN_TEMPLATE = 'uid=%(user)s,ou=users,dc=example,dc=com' + +# Set up the basic group parameters. +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + "ou=django,ou=groups,dc=example,dc=com", + ldap.SCOPE_SUBTREE, + "(objectClass=groupOfNames)", +) +AUTH_LDAP_GROUP_TYPE = GroupOfNamesType(name_attr="cn") + +# Simple group restrictions +AUTH_LDAP_REQUIRE_GROUP = "cn=enabled,ou=django,ou=groups,dc=example,dc=com" +AUTH_LDAP_DENY_GROUP = "cn=disabled,ou=django,ou=groups,dc=example,dc=com" + +# Populate the Django user from the LDAP directory. +AUTH_LDAP_USER_ATTR_MAP = { + "first_name": "givenName", + "last_name": "sn", + "email": "mail", +} + +AUTH_LDAP_USER_FLAGS_BY_GROUP = { + "is_active": "cn=active,ou=django,ou=groups,dc=example,dc=com", + "is_staff": "cn=staff,ou=django,ou=groups,dc=example,dc=com", + "is_superuser": "cn=superuser,ou=django,ou=groups,dc=example,dc=com", +} + +# This is the default, but I like to be explicit. +AUTH_LDAP_ALWAYS_UPDATE_USER = True + +# Use LDAP group membership to calculate group permissions. +AUTH_LDAP_FIND_GROUP_PERMS = True + +# Cache distinguished names and group memberships for an hour to minimize +# LDAP traffic. +AUTH_LDAP_CACHE_TIMEOUT = 3600 diff --git a/Submodules/IntelOwl/configuration/nginx/django_server.conf b/Submodules/IntelOwl/configuration/nginx/django_server.conf new file mode 100644 index 0000000..8a79948 --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/django_server.conf @@ -0,0 +1,29 @@ +server { + listen 80; + + server_name intel_owl.honeynet.org; + + charset utf-8; + + location = /favicon.ico { + access_log off; + log_not_found off; + } + + # All requests to the Django/UWSGI server. + location / { + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Url-Scheme $scheme; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://uwsgi:8001; + client_max_body_size 100m; + } + + # Websocket + include websocket.conf; + + # Error pages + include errors.conf; +} diff --git a/Submodules/IntelOwl/configuration/nginx/errors.conf b/Submodules/IntelOwl/configuration/nginx/errors.conf new file mode 100644 index 0000000..7ca2c20 --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/errors.conf @@ -0,0 +1,59 @@ +error_page 500 /500.json; +location /500.json { + return 500 '{"code":500,"message":"Internal Server Error"}'; +} + +error_page 502 /502.json; +location /502.json { + return 502 '{"code":502,"message":"Bad Gateway"}'; +} + +error_page 503 /503.json; +location /503.json { + return 503 '{"code":503,"message":"Service Temporarily Unavailable"}'; +} + +error_page 504 /504.json; +location /504.json { + return 504 '{"code":504,"message":"Gateway Timeout"}'; +} + +error_page 400 /400.json; +location /400.json { + return 400 '{"code":400,"message":"Bad Request"}'; +} + +error_page 401 /401.json; +location /401.json { + return 401 '{"code":401,"message":"Unauthorized"}'; +} + +error_page 403 /403.json; +location /403.json { + return 403 '{"code":403,"message": "Forbidden"}'; +} + +error_page 404 /404.json; +location /404.json { + return 404 '{"code":404,"message":"Not Found"}'; +} + +error_page 408 /408.json; +location /408.json { + return 408 '{"code":408,"message":"Request Timeout}'; +} + +error_page 413 /413.json; +location /413.json { + return 413 '{"code":413,"message":"Request Entity Too Large"}'; +} + +error_page 418 /418.json; +location /418.json { + return 418 '{"code":418,"message":"I\'m a teapot"}'; +} + +error_page 429 /429.json; +location /429.json { + return 429 '{"code":429,"message":"Too Many Requests"}'; +} \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/nginx/flower_http.conf b/Submodules/IntelOwl/configuration/nginx/flower_http.conf new file mode 100644 index 0000000..a2a62fd --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/flower_http.conf @@ -0,0 +1,20 @@ +# flower +server { + listen 5555; + + server_name intel_owl.honeynet.org; + + server_tokens off; + + charset utf-8; + location / { + proxy_pass http://flower:5555; + proxy_set_header Host $host; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + auth_basic "Restricted"; + auth_basic_user_file /etc/nginx/shared/.htpasswd; + } +} \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/nginx/flower_https.conf b/Submodules/IntelOwl/configuration/nginx/flower_https.conf new file mode 100644 index 0000000..3b0f3a7 --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/flower_https.conf @@ -0,0 +1,25 @@ +# flower +server { + listen 5555 ssl; + ssl on; + ssl_protocols TLSv1.2; + ssl_certificate certificate_chain.chain.crt; + ssl_certificate_key private_key.key; + + server_name intel_owl.honeynet.org; + + server_tokens off; + + charset utf-8; + location / { + proxy_pass http://flower:5555; + proxy_set_header Host $host; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + auth_basic "Restricted"; + auth_basic_user_file /etc/nginx/shared/.htpasswd; + } + +} \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/nginx/http.conf b/Submodules/IntelOwl/configuration/nginx/http.conf new file mode 100644 index 0000000..ba2da60 --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/http.conf @@ -0,0 +1,19 @@ +# the upstream component nginx needs to connect to +upstream django { + server uwsgi:8001 fail_timeout=30s; +} + +limit_req_zone $binary_remote_addr zone=adminlimit:10m rate=1r/s; + +server { + listen 80; + server_name intelowl.honeynet.org; + + server_tokens off; + + # Locations + include locations.conf; + + # Error pages + include errors.conf; +} diff --git a/Submodules/IntelOwl/configuration/nginx/https.conf b/Submodules/IntelOwl/configuration/nginx/https.conf new file mode 100644 index 0000000..5abfbad --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/https.conf @@ -0,0 +1,31 @@ +# the upstream component nginx needs to connect to +upstream django { + server uwsgi:8001 fail_timeout=30s; +} + +server { + listen 80; + server_name intelowl.honeynet.org; + return 301 https://intelowl.honeynet.org$request_uri; +} + +limit_req_zone $binary_remote_addr zone=adminlimit:10m rate=1r/s; + +server { + listen 443 ssl; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_certificate /usr/local/share/ca-certificates/intelowl.crt; + ssl_certificate_key /etc/ssl/private/intelowl.key; + #ssl_password_file /etc/ssl/private/ssl_passwords.txt; + + server_name intelowl.honeynet.org; + + server_tokens off; + + # Locations + include locations.conf; + + # Error pages + include errors.conf; +} diff --git a/Submodules/IntelOwl/configuration/nginx/locations.conf b/Submodules/IntelOwl/configuration/nginx/locations.conf new file mode 100644 index 0000000..0ce1815 --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/locations.conf @@ -0,0 +1,35 @@ +location = /favicon.ico { + access_log off; + log_not_found off; +} + +location /hc { + return 200; +} + +# All requests to the Django/UWSGI server. +location / { + root /; + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 600; + include uwsgi_params; + client_max_body_size 100m; +} + + +# rate limiting for django admin panel +location ^~/admin { + limit_req zone=adminlimit; + + uwsgi_pass django; + uwsgi_pass_header Authorization; + uwsgi_pass_request_headers on; + uwsgi_read_timeout 600; + include uwsgi_params; + client_max_body_size 100m; +} + +# Websocket +include websocket.conf; diff --git a/Submodules/IntelOwl/configuration/nginx/websocket.conf b/Submodules/IntelOwl/configuration/nginx/websocket.conf new file mode 100644 index 0000000..afefaba --- /dev/null +++ b/Submodules/IntelOwl/configuration/nginx/websocket.conf @@ -0,0 +1,12 @@ +location /ws { + proxy_pass http://daphne:8011; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # timeout on connect should be fine with default value: + # comment the config in case errors will rise and could be related to this config + # proxy_connect_timeout 86400; + proxy_read_timeout 86400; + proxy_send_timeout 86400; +} \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/rabbitmq.conf b/Submodules/IntelOwl/configuration/rabbitmq.conf new file mode 100644 index 0000000..3249a2a --- /dev/null +++ b/Submodules/IntelOwl/configuration/rabbitmq.conf @@ -0,0 +1,3 @@ +# 150 hours, see https://www.rabbitmq.com/consumers.html#acknowledgement-timeout +# the time is long to avoid a lot of critical errors +consumer_timeout = 540000000 \ No newline at end of file diff --git a/Submodules/IntelOwl/configuration/radius_config.py b/Submodules/IntelOwl/configuration/radius_config.py new file mode 100644 index 0000000..dec6278 --- /dev/null +++ b/Submodules/IntelOwl/configuration/radius_config.py @@ -0,0 +1,27 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +RADIUS_SERVER = "localhost" +RADIUS_PORT = 1812 +RADIUS_SECRET = "S3kr3T" + +RADIUS_REMOTE_ROLES = True + +# https://github.com/robgolding/django-radius#customised-functionality +# The get_server method of the backend class is used to determine +# which RADIUS server to authenticate against. +# It can be customised to achieve things like multiple RADIUS servers (realms). +# Set GET_SERVER_CUSTOMISED to True to use this functionality. +GET_SERVER_CUSTOMISED = False + + +def custom_get_server(self, realm): + # /* Custom realm logic here */ + pass + + +# https://github.com/robgolding/django-radius#additional-attributes +# RADIUS_ATTRIBUTES = {} + +# https://github.com/robgolding/django-radius#group-mapping +# RADIUS_CLASS_APP_PREFIX = 'some_project_name' diff --git a/Submodules/IntelOwl/docker/Dockerfile b/Submodules/IntelOwl/docker/Dockerfile new file mode 100644 index 0000000..d68cdc3 --- /dev/null +++ b/Submodules/IntelOwl/docker/Dockerfile @@ -0,0 +1,83 @@ +# Stage 1: Frontend +# Remove this part if it is conflicting with the frontend you are currently testing +FROM node:lts-alpine3.18 as frontend-build + +WORKDIR / +# copy react source code +COPY frontend/ . +# copy version file as an env file +COPY docker/.env .env.local +# install and build +RUN npm install npm@latest --location=global \ + && npm install \ + && PUBLIC_URL=/static/reactapp/ npm run build + +# Stage 2: Backend +FROM python:3.11.7 as backend-build + +ENV PYTHONUNBUFFERED 1 +ENV DJANGO_SETTINGS_MODULE intel_owl.settings +ENV PYTHONPATH /opt/deploy/intel_owl +ENV LOG_PATH /var/log/intel_owl +ARG REPO_DOWNLOADER_ENABLED=true +ARG WATCHMAN=false +ENV watch_logs_cmd "watch -n1 tail -n10 /var/log/intel_owl/django/api_app.log" +ARG PYCTI_VERSION=6.1.0 + +RUN mkdir -p ${LOG_PATH} \ + ${LOG_PATH}/django \ + ${LOG_PATH}/uwsgi \ + ${LOG_PATH}/asgi \ + /opt/deploy/files_required /opt/deploy/configuration /opt/deploy/files_required/blint /opt/deploy/files_required/yara + +# install required packages. some notes about:o +# python3-psycopg2 is required to use PostgresSQL with Django +# apache2-utils is required to execute htpasswd +# tshark is required for Hfinger file analyzer +RUN apt-get update \ + && apt-get install -y --no-install-recommends apt-utils libsasl2-dev libssl-dev netcat-traditional \ + vim libldap2-dev libfuzzy-dev net-tools python3-psycopg2 git apache2-utils tshark \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && pip3 install --no-cache-dir --upgrade pip + +# perl not interactive +ENV PERL_MM_USE_DEFAULT 1 +# msgconvert +RUN cpan -T Email::Outlook::Message + +COPY requirements/project-requirements.txt $PYTHONPATH/project-requirements.txt +COPY requirements/certego-requirements.txt $PYTHONPATH/certego-requirements.txt +WORKDIR $PYTHONPATH + +RUN pip3 install --no-cache-dir --compile -r project-requirements.txt \ + && pip3 install --no-cache-dir pycti==${PYCTI_VERSION} \ + && pip3 install --no-cache-dir --compile -r certego-requirements.txt + +COPY . $PYTHONPATH + +RUN touch ${LOG_PATH}/django/api_app.log ${LOG_PATH}/django/api_app_errors.log \ + && touch ${LOG_PATH}/django/intel_owl.log ${LOG_PATH}/django/intel_owl_errors.log \ + && touch ${LOG_PATH}/django/celery.log ${LOG_PATH}/django/celery_errors.log \ + && touch ${LOG_PATH}/django/django_auth_ldap.log ${LOG_PATH}/django/django_errors.log \ + && touch ${LOG_PATH}/django/certego_saas.log ${LOG_PATH}/django/certego_saas_errors.log \ + && touch ${LOG_PATH}/django/authentication.log ${LOG_PATH}/django/authentication_errors.log \ + && touch ${LOG_PATH}/asgi/daphne.log \ + && chown -R www-data:www-data ${LOG_PATH} /opt/deploy/ \ + # this is cause stringstifer creates this directory during the build and cause celery to crash + && rm -rf /root/.local \ + && ${PYTHONPATH}/docker/scripts/watchman_install.sh \ + # download github stuff + && ${PYTHONPATH}/api_app/analyzers_manager/repo_downloader.sh + +FROM backend-build + +COPY --from=frontend-build /build /var/www/reactapp + + +# quarkengine calls +# HOME_DIR = f"{Path.home()}/.quark-engine/" +# Path(HOME_DIR).mkdir(parents=True, exist_ok=True) +# so we have to set the home env variable to allow to create its directory +ENV HOME ${PYTHONPATH} + diff --git a/Submodules/IntelOwl/docker/Dockerfile_nginx b/Submodules/IntelOwl/docker/Dockerfile_nginx new file mode 100644 index 0000000..73ef0a2 --- /dev/null +++ b/Submodules/IntelOwl/docker/Dockerfile_nginx @@ -0,0 +1,8 @@ +FROM library/nginx:1.26.0-alpine + +ENV NGINX_LOG_DIR /var/log/nginx +# this is to avoid having these logs redirected to stdout/stderr +RUN rm $NGINX_LOG_DIR/access.log $NGINX_LOG_DIR/error.log && touch $NGINX_LOG_DIR/access.log $NGINX_LOG_DIR/error.log +VOLUME /var/log/nginx + +HEALTHCHECK --interval=3s --start-period=2s --timeout=2s --retries=5 CMD curl --fail http://localhost/hc || exit 1 \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/bin/index.html b/Submodules/IntelOwl/docker/bin/index.html new file mode 100644 index 0000000..c2ad44a --- /dev/null +++ b/Submodules/IntelOwl/docker/bin/index.html @@ -0,0 +1,608 @@ + + + + + + + + + + + +Embedded binary list - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Embedded binary list

+

Osslsigncode

+

We embedded the compiled version for Ubuntu for that can be retrieved from its original repo here.

+

We decided to do not use the version shipped by default Ubuntu packages because it were too old (2.1)

+

At the last time of writing we uploaded the version 2.6-dev

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/bin/osslsigncode b/Submodules/IntelOwl/docker/bin/osslsigncode new file mode 100644 index 0000000..ca002ca Binary files /dev/null and b/Submodules/IntelOwl/docker/bin/osslsigncode differ diff --git a/Submodules/IntelOwl/docker/ci.override.yml b/Submodules/IntelOwl/docker/ci.override.yml new file mode 100644 index 0000000..c64f176 --- /dev/null +++ b/Submodules/IntelOwl/docker/ci.override.yml @@ -0,0 +1,72 @@ +version: '3.8' + +services: + postgres: + env_file: + - env_file_postgres_template + deploy: + resources: + limits: + cpus: '1' + memory: 2000M + + uwsgi: + build: + context: .. + dockerfile: docker/Dockerfile + args: + REPO_DOWNLOADER_ENABLED: ${REPO_DOWNLOADER_ENABLED} + image: intelowlproject/intelowl:ci + env_file: + - env_file_app_ci + + + daphne: + image: intelowlproject/intelowl:ci + env_file: + - env_file_app_ci + deploy: + resources: + limits: + cpus: '0.50' + memory: 200M + + nginx: + build: + context: .. + dockerfile: docker/Dockerfile_nginx + image: intelowlproject/intelowl_nginx:ci + volumes: + - ../configuration/nginx/http.conf:/etc/nginx/conf.d/default.conf + deploy: + resources: + limits: + cpus: '0.50' + memory: 200M + + celery_beat: + image: intelowlproject/intelowl:ci + env_file: + - env_file_app_ci + deploy: + resources: + limits: + cpus: '0.50' + memory: 200M + + celery_worker_default: + image: intelowlproject/intelowl:ci + env_file: + - env_file_app_ci + deploy: + resources: + limits: + cpus: '0.50' + memory: 200M + + redis: + deploy: + resources: + limits: + cpus: '0.50' + memory: 200M \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/default.yml b/Submodules/IntelOwl/docker/default.yml new file mode 100644 index 0000000..5dc13de --- /dev/null +++ b/Submodules/IntelOwl/docker/default.yml @@ -0,0 +1,121 @@ +version: '3.8' + +x-no-healthcheck: &no-healthcheck + healthcheck: + disable: true + +services: + uwsgi: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_uwsgi + volumes: + - ../configuration/intel_owl.ini:/etc/uwsgi/sites/intel_owl.ini # uwsgi configuration file + - ../configuration:/opt/deploy/intel_owl/configuration + - generic_logs:/var/log/intel_owl + - ../frontend/public/env.js:/var/www/reactapp/env.js # this is needed because we have to mount the file from the server to the image BEFORE staticfiles + - static_content:/opt/deploy/intel_owl/static + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/uwsgi.sh + expose: + - "8001" + env_file: + - env_file_app + - .env + healthcheck: + test: [ "CMD-SHELL", "nc -z localhost 8001 || exit 1" ] + interval: 5s + timeout: 2s + start_period: 300s + retries: 2 + + daphne: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_daphne + restart: unless-stopped + volumes: + - generic_logs:/var/log/intel_owl + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/daphne.sh + expose: + - "8011" + env_file: + - env_file_app + healthcheck: + test: ["CMD-SHELL", "nc -z localhost 8011 || exit 1"] + interval: 5s + timeout: 2s + start_period: 2s + retries: 6 + depends_on: + uwsgi: + condition: service_healthy + + nginx: + image: intelowlproject/intelowl_nginx:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_nginx + restart: unless-stopped + volumes: + - ../configuration/nginx/websocket.conf:/etc/nginx/websocket.conf + - ../configuration/nginx/errors.conf:/etc/nginx/errors.conf + - ../configuration/nginx/http.conf:/etc/nginx/conf.d/default.conf + - ../configuration/nginx/locations.conf:/etc/nginx/locations.conf + - nginx_logs:/var/log/nginx + - static_content:/var/www/static + ports: + - "80:80" + depends_on: + uwsgi: + condition: service_healthy + daphne: + condition: service_healthy + + celery_beat: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_celery_beat + restart: unless-stopped + volumes: + - ../configuration:/opt/deploy/intel_owl/configuration + - generic_logs:/var/log/intel_owl + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/celery_beat.sh + env_file: + - env_file_app + <<: *no-healthcheck + depends_on: + uwsgi: + condition: service_healthy + + + celery_worker_default: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_celery_worker_default + restart: unless-stopped + stop_grace_period: 3m + volumes: + - ../configuration:/opt/deploy/intel_owl/configuration + - generic_logs:/var/log/intel_owl + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/celery_default.sh + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + env_file: + - env_file_app + depends_on: + uwsgi: + condition: service_healthy + <<: *no-healthcheck + + +volumes: + postgres_data: + nginx_logs: + generic_logs: + static_content: + shared_files: diff --git a/Submodules/IntelOwl/docker/elasticsearch.override.yml b/Submodules/IntelOwl/docker/elasticsearch.override.yml new file mode 100644 index 0000000..7e8fc5b --- /dev/null +++ b/Submodules/IntelOwl/docker/elasticsearch.override.yml @@ -0,0 +1,21 @@ +version: '3.8' + +services: + uwsgi: + depends_on: + - elasticsearch + + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0 + environment: + - "discovery.type=single-node" + + kibana: + image: docker.elastic.co/kibana/kibana:7.17.0 + environment: + ELASTICSEARCH_HOSTS: '["http://elasticsearch:9200"]' + ports: + - '5601:5601' + depends_on: + - elasticsearch + diff --git a/Submodules/IntelOwl/docker/entrypoints/celery_beat.sh b/Submodules/IntelOwl/docker/entrypoints/celery_beat.sh new file mode 100644 index 0000000..4c05594 --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/celery_beat.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done +ARGUMENTS="-A intel_owl.celery beat --uid www-data --gid www-data --pidfile= --schedule=/tmp/celerybeat-schedule --scheduler django_celery_beat.schedulers:DatabaseScheduler" +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + echo "Running celery with autoreload" + python3 manage.py celery_reload -c "$ARGUMENTS" +else + /usr/local/bin/celery $ARGUMENTS +fi \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/entrypoints/celery_default.sh b/Submodules/IntelOwl/docker/entrypoints/celery_default.sh new file mode 100644 index 0000000..14b7a11 --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/celery_default.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done + +echo "environment: $STAGE" +if [ "$STAGE" = "ci" ] +then + worker_number=1 +else + # default is prod + worker_number=8 +fi + +ARGUMENTS="-A intel_owl.celery worker -n worker_default --uid www-data --gid www-data --time-limit=10000 --pidfile= -c $worker_number -Ofair -Q default,broadcast,config -E --without-gossip" +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + echo "Running celery with autoreload" + python3 manage.py celery_reload -c "$ARGUMENTS" +else + /usr/local/bin/celery $ARGUMENTS +fi \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/entrypoints/celery_ingestor.sh b/Submodules/IntelOwl/docker/entrypoints/celery_ingestor.sh new file mode 100644 index 0000000..5150de6 --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/celery_ingestor.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done +ARGUMENTS="-A intel_owl.celery worker -n worker_ingestor --uid www-data --gid www-data --time-limit=40000 --pidfile= -Ofair -Q ingestor,broadcast,config -E --autoscale=1,15 --without-gossip" +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + echo "Running celery with autoreload" + python3 manage.py celery_reload -c "$ARGUMENTS" +else + /usr/local/bin/celery $ARGUMENTS +fi \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/entrypoints/celery_local.sh b/Submodules/IntelOwl/docker/entrypoints/celery_local.sh new file mode 100644 index 0000000..28164b2 --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/celery_local.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done + +ARGUMENTS="-A intel_owl.celery worker -n worker_local --uid www-data --time-limit=10000 --gid www-data --pidfile= -Ofair -Q local,broadcast,config -E --without-gossip" +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + echo "Running celery with autoreload" + python3 manage.py celery_reload -c "$ARGUMENTS" +else + /usr/local/bin/celery $ARGUMENTS +fi \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/entrypoints/celery_long.sh b/Submodules/IntelOwl/docker/entrypoints/celery_long.sh new file mode 100644 index 0000000..68506ba --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/celery_long.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done +ARGUMENTS="-A intel_owl.celery worker -n worker_long --uid www-data --gid www-data --time-limit=40000 --pidfile= -Ofair -Q long,broadcast,config -E --without-gossip" +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + echo "Running celery with autoreload" + python3 manage.py celery_reload -c "$ARGUMENTS" +else + /usr/local/bin/celery $ARGUMENTS +fi \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/entrypoints/daphne.sh b/Submodules/IntelOwl/docker/entrypoints/daphne.sh new file mode 100644 index 0000000..682d16b --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/daphne.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done + +# verbosity param levels: https://github.com/django/daphne/blob/df0680c9ad699817725e18a9264df17fff2927da/daphne/cli.py#L213 +# not useful to improve logging +/usr/local/bin/daphne --proxy-headers --access-log /var/log/intel_owl/asgi/daphne.log -p 8011 -b 0.0.0.0 --no-server-name --application-close-timeout 60 --ping-interval 30 --ping-timeout 35 intel_owl.asgi:application diff --git a/Submodules/IntelOwl/docker/entrypoints/flower.sh b/Submodules/IntelOwl/docker/entrypoints/flower.sh new file mode 100644 index 0000000..ac8395b --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/flower.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done + +echo "------------------------------" +echo "DEBUG: ${DEBUG}" +echo "DJANGO_TEST_SERVER: ${DJANGO_TEST_SERVER}" +echo "------------------------------" + +# this is required for retrocompatibility (this env variable did not exist before v3.0.2) +if [ -z "${BROKER_URL_API}" ] +then + BROKER_URL_API="http://guest:guest@rabbitmq:15672/api/" +fi + +if [ -z "${BROKER_URL}" ] +then + BROKER_URL="amqp://guest:guest@rabbitmq:5672" +fi + +if [ -z "${FLOWER_USR}" ] +then + FLOWER_USR="flower" +fi + +if [ -z "${FLOWER_PWD}" ] +then + FLOWER_PWD="flower" +fi + +CMD="/usr/local/bin/celery -A intel_owl.celery --broker ${BROKER_URL} flower --broker_api=${BROKER_URL_API} --max_tasks=1000 --max_workers=500" +htpasswd -cb /opt/deploy/shared_htpasswd/.htpasswd ${FLOWER_USER} ${FLOWER_PWD} + +if [[ ${DEBUG} == "True" ]] && [[ ${DJANGO_TEST_SERVER} == "True" ]]; +then + ${CMD} --debug +else + ${CMD} +fi diff --git a/Submodules/IntelOwl/docker/entrypoints/uwsgi.sh b/Submodules/IntelOwl/docker/entrypoints/uwsgi.sh new file mode 100644 index 0000000..21f5626 --- /dev/null +++ b/Submodules/IntelOwl/docker/entrypoints/uwsgi.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +until cd /opt/deploy/intel_owl +do + echo "Waiting for server volume..." +done +sudo su www-data -c "mkdir -p /var/log/intel_owl/django /var/log/intel_owl/uwsgi /var/log/intel_owl/asgi /opt/deploy/intel_owl/files_required/blint /opt/deploy/intel_owl/files_required/yara" + +# Apply database migrations +echo "Waiting for db to be ready..." +# makemigrations is needed only for the durin package. +# The customization of the parameters is not applied until the migration is done +python manage.py makemigrations durin +python manage.py makemigrations rest_email_auth +python manage.py createcachetable +# fake-initial does not fake the migration if the table does not exist +python manage.py migrate --fake-initial +if ! python manage.py migrate --check + then + echo "Issue with migration exiting" + exit 1 +fi +# Collect static files +python manage.py collectstatic --noinput +echo "------------------------------" +echo "DEBUG: " $DEBUG +echo "DJANGO_TEST_SERVER: " $DJANGO_TEST_SERVER +echo "------------------------------" +CHANGELOG_NOTIFICATION_COMMAND='python manage.py changelog_notification .github/CHANGELOG.md INTELOWL --number-of-releases 3' + +if [[ $DEBUG == "True" ]] && [[ $DJANGO_TEST_SERVER == "True" ]]; +then + $CHANGELOG_NOTIFICATION_COMMAND --debug + python manage.py runserver 0.0.0.0:8001 +else + $CHANGELOG_NOTIFICATION_COMMAND + /usr/local/bin/uwsgi --ini /etc/uwsgi/sites/intel_owl.ini --stats 127.0.0.1:1717 --stats-http +fi diff --git a/Submodules/IntelOwl/docker/env_file_app_ci b/Submodules/IntelOwl/docker/env_file_app_ci new file mode 100644 index 0000000..acb8e20 --- /dev/null +++ b/Submodules/IntelOwl/docker/env_file_app_ci @@ -0,0 +1,66 @@ +# !!!! This file is used only to give required variables to the CI Build +DJANGO_SECRET=supersecret +# Required Secrets +DB_HOST=postgres +DB_PORT=5432 +DB_USER=user +DB_PASSWORD=password +DB_NAME=intel_owl_db + +# Config variables +OLD_JOBS_RETENTION_DAYS=2 +INTELOWL_WEB_CLIENT_DOMAIN=localhost +# Storage +LOCAL_STORAGE=True + +# OAuth2 +GOOGLE_CLIENT_ID=test_intelowl.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=the_supreme_secret + +# AWS +AWS_STORAGE_BUCKET_NAME= +AWS_IAM_ACCESS = False +AWS_SECRETS=False +AWS_SQS=False +AWS_REGION=eu-central-1 +## to use if no IAM credentials are provide +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= + +# Elastic Search Configuration +ELASTICSEARCH_DSL_ENABLED=False +ELASTICSEARCH_DSL_HOST= +ELASTICSEARCH_DSL_NO_OF_SHARDS=1 +ELASTICSEARCH_DSL_NO_OF_REPLICAS=0 + +ELASTICSEARCH_BI_ENABLED=False +ELASTICSEARCH_BI_HOST= +ELASTICSEARCH_BI_INDEX=intelowl-bi + + +# Test tokens +TEST_IP=8.8.8.8 +TEST_DOMAIN=www.google.com +TEST_URL=https://www.google.com/search?test +TEST_MD5=446c5fbb11b9ce058450555c1c27153c +TEST_GENERIC=email@example.com + + +# other variables +STAGE="ci" +DEBUG=True +DISABLE_LOGGING_TEST=True +MOCK_CONNECTIONS=True +HTTPS_ENABLED=not_enabled +DJANGO_SETTINGS_MODULE=intel_owl.settings +COVERAGE_PROCESS_START=.coveragerc + +# broker configuration +BROKER_URL=redis://redis:6379/1 +WEBSOCKETS_URL=redis://redis:6379/0 + +# drf-recaptcha +USE_RECAPTCHA=False +RECAPTCHA_SECRET_KEY="testkey" +# crons +REPO_DOWNLOADER_ENABLED=False \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/env_file_app_template b/Submodules/IntelOwl/docker/env_file_app_template new file mode 100644 index 0000000..ec56bc5 --- /dev/null +++ b/Submodules/IntelOwl/docker/env_file_app_template @@ -0,0 +1,92 @@ +# Required Secrets +DJANGO_SECRET= +DB_HOST=postgres +DB_PORT=5432 +DB_USER=user +DB_PASSWORD=password +DB_SSL=False +DB_NAME=intel_owl_db + +# Additional Config variables +# jobs older than this would be flushed from the database periodically. Default: 14 days +OLD_JOBS_RETENTION_DAYS=14 +# used for generating links to web client e.g. job results page; Default: localhost +INTELOWL_WEB_CLIENT_DOMAIN=localhost +# used for automated correspondence from the site manager +DEFAULT_FROM_EMAIL= +# used for correspondence with users +DEFAULT_EMAIL= +# Storage +LOCAL_STORAGE=True + +# OAuth2 +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# SMTP backend +EMAIL_HOST= +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +EMAIL_PORT= +EMAIL_USE_TLS=False +EMAIL_USE_SSL=False + +# AWS +## S3 storage +AWS_STORAGE_BUCKET_NAME= +AWS_IAM_ACCESS=False +### to use if no IAM credentials are provided +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +## secrets, broker and region +AWS_SECRETS=False +AWS_SQS=False +AWS_USER_NUMBER= +AWS_REGION=eu-central-1 +# IAM ROLE for RDS +AWS_RDS_IAM_ROLE=False +## to use for sending mail with SES +AWS_SES=False + +# Uploads +SLACK_TOKEN= +DEFAULT_SLACK_CHANNEL= + +# Elastic Search Configuration +ELASTICSEARCH_DSL_ENABLED=False +ELASTICSEARCH_DSL_HOST= +# consult to: https://django-elasticsearch-dsl.readthedocs.io/en/latest/settings.html +ELASTICSEARCH_DSL_NO_OF_SHARDS=1 +ELASTICSEARCH_DSL_NO_OF_REPLICAS=0 + +ELASTICSEARCH_BI_ENABLED=False +ELASTICSEARCH_BI_HOST= +ELASTICSEARCH_BI_INDEX=intelowl-bi + +# Test tokens +TEST_IP=8.8.8.8 +TEST_DOMAIN=www.google.com +TEST_URL=https://www.google.com/search?test +TEST_MD5=446c5fbb11b9ce058450555c1c27153c + +# other variables +STAGE="production" +DEBUG=False +LDAP_ENABLED=False +DISABLE_LOGGING_TEST=False +MOCK_CONNECTIONS=False +HTTPS_ENABLED=not_enabled +RADIUS_AUTH_ENABLED=False +# True for public deployment, False for internal deployment +PUBLIC_DEPLOYMENT=False +# broker configuration +BROKER_URL=redis://redis:6379/1 +WEBSOCKETS_URL=redis://redis:6379/0 + +FLOWER_USER=flower +FLOWER_PWD=flower + +USE_RECAPTCHA=False +# drf-recaptcha +## localhost, internal deployments +RECAPTCHA_SECRET_KEY="" diff --git a/Submodules/IntelOwl/docker/env_file_integrations_template b/Submodules/IntelOwl/docker/env_file_integrations_template new file mode 100644 index 0000000..14b5241 --- /dev/null +++ b/Submodules/IntelOwl/docker/env_file_integrations_template @@ -0,0 +1,6 @@ +### ---- Environment variables for additional integrations --- +### All env variables for docker based integrations should be added here +### IMPORTANT: don't change these values unless you know what you are doing. It can have breaking changes! + +# Applies to all integrations, choose from: INFO (recommended), ERROR, DEBUG. +LOG_LEVEL=INFO \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/env_file_postgres_template b/Submodules/IntelOwl/docker/env_file_postgres_template new file mode 100644 index 0000000..31d7979 --- /dev/null +++ b/Submodules/IntelOwl/docker/env_file_postgres_template @@ -0,0 +1,3 @@ +POSTGRES_PASSWORD=password +POSTGRES_USER=user +POSTGRES_DB=intel_owl_db \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/flower.override.yml b/Submodules/IntelOwl/docker/flower.override.yml new file mode 100644 index 0000000..575f773 --- /dev/null +++ b/Submodules/IntelOwl/docker/flower.override.yml @@ -0,0 +1,48 @@ +version: "3.8" + +services: + uwsgi: + environment: + - BROKER_URL_API=http://guest:guest@rabbitmq:15672/api/ + + rabbitmq: + image: library/rabbitmq:3.11-management-alpine + hostname: rabbitmq + container_name: intelowl_rabbitmq + logging: + driver: none + expose: + - "15672" + + flower: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_flower + hostname: flower + restart: unless-stopped + stop_grace_period: 3m + volumes: + - generic_logs:/var/log/intel_owl + - shared_htpasswd:/opt/deploy/shared_htpasswd + expose: + - "5555" + depends_on: + - rabbitmq + - uwsgi + entrypoint: + - ./docker/entrypoints/flower.sh + env_file: + - env_file_app + healthcheck: + disable: true + + nginx: + volumes: + - shared_htpasswd:/etc/nginx/shared + - ../configuration/nginx/flower_http.conf:/etc/nginx/conf.d/flower.conf + ports: + - "5555:5555" + depends_on: + - rabbitmq + +volumes: + shared_htpasswd: \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/https.override.yml b/Submodules/IntelOwl/docker/https.override.yml new file mode 100644 index 0000000..931d5c1 --- /dev/null +++ b/Submodules/IntelOwl/docker/https.override.yml @@ -0,0 +1,10 @@ +version: '3.8' + +services: + nginx: + volumes: + - ../configuration/nginx/https.conf:/etc/nginx/conf.d/default.conf + - /usr/local/share/ca-certificates:/usr/local/share/ca-certificates + - /etc/ssl/private:/etc/ssl/private + ports: + - "443:443" \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/multi-queue.override.yml b/Submodules/IntelOwl/docker/multi-queue.override.yml new file mode 100644 index 0000000..e660fb1 --- /dev/null +++ b/Submodules/IntelOwl/docker/multi-queue.override.yml @@ -0,0 +1,67 @@ +version: '3.8' + +x-no-healthcheck: &no-healthcheck + healthcheck: + disable: true + +services: + uwsgi: + environment: + - CELERY_QUEUES=default,local,long + + celery_worker_local: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_celery_worker_local + restart: unless-stopped + hostname: celery_worker_local + stop_grace_period: 3m + volumes: + - ../configuration:/opt/deploy/intel_owl/configuration + - generic_logs:/var/log/intel_owl + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/celery_local.sh + env_file: + - env_file_app + depends_on: + uwsgi: + condition: service_healthy + <<: *no-healthcheck + + celery_worker_long: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_celery_worker_long + hostname: celery_worker_long + restart: unless-stopped + stop_grace_period: 3m + volumes: + - ../configuration:/opt/deploy/intel_owl/configuration + - generic_logs:/var/log/intel_owl + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/celery_long.sh + env_file: + - env_file_app + depends_on: + uwsgi: + condition: service_healthy + <<: *no-healthcheck + + celery_worker_ingestor: + image: intelowlproject/intelowl:${REACT_APP_INTELOWL_VERSION} + container_name: intelowl_celery_worker_ingestor + hostname: celery_worker_ingestor + restart: unless-stopped + stop_grace_period: 3m + volumes: + - ../configuration:/opt/deploy/intel_owl/configuration + - generic_logs:/var/log/intel_owl + - shared_files:/opt/deploy/files_required + entrypoint: + - ./docker/entrypoints/celery_ingestor.sh + env_file: + - env_file_app + depends_on: + uwsgi: + condition: service_healthy + <<: *no-healthcheck diff --git a/Submodules/IntelOwl/docker/nfs.override.yml b/Submodules/IntelOwl/docker/nfs.override.yml new file mode 100644 index 0000000..bdcdd9a --- /dev/null +++ b/Submodules/IntelOwl/docker/nfs.override.yml @@ -0,0 +1,33 @@ +version: '3.8' + +services: + uwsgi: + volumes: + - nfs_files:/opt/deploy/files_required + environment: + - NFS=True + + celery_worker_default: + volumes: + - nfs_files:/opt/deploy/files_required + environment: + - NFS=True + ports: + - "2049:2049" + - "2049:2049/udp" + + celery_beat: + volumes: + - nfs_files:/opt/deploy/files_required + environment: + - NFS=True + + + +volumes: + nfs_files: + driver: "local" + driver_opts: + type: "nfs" + o: "addr=${NFS_ADDRESS},nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2" + device: ":/" \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/postgres.override.yml b/Submodules/IntelOwl/docker/postgres.override.yml new file mode 100644 index 0000000..f1dcec6 --- /dev/null +++ b/Submodules/IntelOwl/docker/postgres.override.yml @@ -0,0 +1,29 @@ +version: "3.8" + +services: + + postgres: + image: library/postgres:16-alpine + container_name: intelowl_postgres + volumes: + - postgres_data:/var/lib/postgresql/data/ + env_file: + - ./env_file_postgres + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB" ] + interval: 5s + timeout: 2s + retries: 6 + start_period: 3s + + + uwsgi: + depends_on: + postgres: + condition: service_healthy + + + celery_worker_default: + depends_on: + postgres: + condition: service_healthy diff --git a/Submodules/IntelOwl/docker/rabbitmq.override.yml b/Submodules/IntelOwl/docker/rabbitmq.override.yml new file mode 100644 index 0000000..d59fe20 --- /dev/null +++ b/Submodules/IntelOwl/docker/rabbitmq.override.yml @@ -0,0 +1,24 @@ +version: "3.8" + +services: + rabbitmq: + image: library/rabbitmq:3.11-alpine + container_name: intelowl_rabbitmq + volumes: + - ../configuration/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf + logging: + driver: none + + uwsgi: + environment: + - BROKER_URL="amqp://guest:guest@rabbitmq:5672" + depends_on: + - rabbitmq + + celery_worker_default: + environment: + - BROKER_URL="amqp://guest:guest@rabbitmq:5672" + + celery_beat: + environment: + - BROKER_URL="amqp://guest:guest@rabbitmq:5672" diff --git a/Submodules/IntelOwl/docker/redis.override.yml b/Submodules/IntelOwl/docker/redis.override.yml new file mode 100644 index 0000000..8fd0947 --- /dev/null +++ b/Submodules/IntelOwl/docker/redis.override.yml @@ -0,0 +1,42 @@ +version: "3.8" +services: + uwsgi: + depends_on: + redis: + condition: service_healthy + environment: + - BROKER_URL=redis://redis:6379/1 + - WEBSOCKETS_URL=redis://redis:6379/0 + + daphne: + depends_on: + redis: + condition: service_healthy + environment: + - BROKER_URL=redis://redis:6379/1 + - WEBSOCKETS_URL=redis://redis:6379/0 + + redis: + image: library/redis:6.2.7-alpine + container_name: intelowl_redis + hostname: redis + restart: unless-stopped + volumes: + - generic_logs:/var/log/intel_owl + expose: + - "6379" + healthcheck: + test: test $$(redis-cli -h '127.0.0.1' ping) = 'PONG' + + celery_beat: + depends_on: + redis: + condition: service_healthy + environment: + - BROKER_URL=redis://redis:6379/1 + - WEBSOCKETS_URL=redis://redis:6379/0 + + celery_worker_default: + environment: + - BROKER_URL=redis://redis:6379/1 + - WEBSOCKETS_URL=redis://redis:6379/0 \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/add_license.py b/Submodules/IntelOwl/docker/scripts/add_license.py new file mode 100644 index 0000000..eb4f627 --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/add_license.py @@ -0,0 +1,47 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +import sys +from pathlib import PosixPath + +header0 = ( + "# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl" +) +header1 = "# See the file 'LICENSE' for copying permission." + + +# arguments: BASE_DIR [run] +if __name__ == "__main__": + if len(sys.argv) == 1: + print("First argument is the base directory to check") + sys.exit(1) + if len(sys.argv) > 3: + print("Too many argument") + sys.exit(1) + if len(sys.argv) == 3 and sys.argv[2] != "run": + print("Argument must be `run` or empty") + base_path = sys.argv[1] + base_path = PosixPath(base_path) + if not base_path.exists(): + print(f"{str(base_path)} does not exists") + if not base_path.is_dir(): + print(f"{str(base_path)} is not a directory") + for file in base_path.rglob("*.py"): + print(f"{file}") + if file.stem == "__init__": + print("\tSkipping") + continue + with open(file, "r+", encoding="utf_8") as f: + lines = f.readlines() + if not ( + lines[0].strip() != header0.strip() + and lines[1].strip() != header1.strip() + ): + print("\tSkipping") + continue + else: + print("\tAdding header") + if len(sys.argv) == 3 and sys.argv[2] == "run": + with open(file, "w", encoding="utf_8") as f: + print("\tWritten") + f.writelines([header0, "\n", header1, "\n", "\n"] + lines) diff --git a/Submodules/IntelOwl/docker/scripts/cron/application_restart b/Submodules/IntelOwl/docker/scripts/cron/application_restart new file mode 100644 index 0000000..c9848bf --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/cron/application_restart @@ -0,0 +1 @@ +0 2 * * 0 root /opt/deploy/intel_owl/start test down --all_analyzers && /opt/deploy/intel_owl/start test up --all_analyzers -- -d \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/cron/update_repositories b/Submodules/IntelOwl/docker/scripts/cron/update_repositories new file mode 100644 index 0000000..420560f --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/cron/update_repositories @@ -0,0 +1,6 @@ +0 1 * * 3 root python3 docker exec -ti $(docker ps | grep intelowl_celery_worker_default | awk '{print $1}') python3 manage.py update_analyzer MaxMindGeoIP +5 */6 * * * root python3 docker exec -ti $(docker ps | grep intelowl_celery_worker_default | awk '{print $1}') python3 manage.py update_analyzer TalosReputation +5 */6 * * * root python3 docker exec -ti $(docker ps | grep intelowl_celery_worker_default | awk '{print $1}') python3 manage.py update_analyzer PhishingArmy +*/10 * * * * root python3 docker exec -ti $(docker ps | grep intelowl_celery_worker_default | awk '{print $1}') python3 manage.py update_analyzer TorProject +0 4 * * * root python3 docker exec -ti $(docker ps | grep intelowl_celery_worker_default | awk '{print $1}') python3 manage.py update_analyzer Yara +0 3 * * 2,5 root python3 docker exec -ti $(docker ps | grep intelowl_celery_worker_default | awk '{print $1}') python3 manage.py update_analyzer Quark_Engine diff --git a/Submodules/IntelOwl/docker/scripts/initdb.sh b/Submodules/IntelOwl/docker/scripts/initdb.sh new file mode 100644 index 0000000..34ae827 --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/initdb.sh @@ -0,0 +1,4 @@ +docker exec intelowl_uwsgi python3 manage.py makemigrations +docker exec intelowl_uwsgi python3 manage.py migrate +docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser \ +--username admin --email admin@admin.com --first_name admin --last_name admin diff --git a/Submodules/IntelOwl/docker/scripts/install_crontab.sh b/Submodules/IntelOwl/docker/scripts/install_crontab.sh new file mode 100644 index 0000000..5826d0a --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/install_crontab.sh @@ -0,0 +1 @@ +cp cron/* /etc/cron.d \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/install_logrotate.sh b/Submodules/IntelOwl/docker/scripts/install_logrotate.sh new file mode 100644 index 0000000..c7dceec --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/install_logrotate.sh @@ -0,0 +1 @@ +sudo cp -r logrotate /etc/logrotate.d \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/logrotate/application b/Submodules/IntelOwl/docker/scripts/logrotate/application new file mode 100644 index 0000000..08de8ee --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/logrotate/application @@ -0,0 +1,11 @@ +/var/lib/docker/volumes/*_logs/_data/*/*.log { + daily + missingok + rotate 2 + size 10M + compress + delaycompress + notifempty + create 0644 www-data www-data + copytruncate +} \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/logrotate/docker b/Submodules/IntelOwl/docker/scripts/logrotate/docker new file mode 100644 index 0000000..894631c --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/logrotate/docker @@ -0,0 +1,11 @@ +/var/log/docker/*.log { + daily + missingok + rotate 2 + size 10M + compress + delaycompress + notifempty + create 0655 syslog adm + copytruncate +} diff --git a/Submodules/IntelOwl/docker/scripts/logrotate/nginx b/Submodules/IntelOwl/docker/scripts/logrotate/nginx new file mode 100644 index 0000000..d4f1ccc --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/logrotate/nginx @@ -0,0 +1,20 @@ +/var/lib/docker/volumes/*_nginx/_data/*.log { + daily + missingok + rotate 8 + size 1M + compress + delaycompress + notifempty + create 0644 www-data www-data + sharedscripts + prerotate + if [ -d /etc/logrotate.d/httpd-prerotate ]; then \ + run-parts /etc/logrotate.d/httpd-prerotate; \ + fi \ + endscript + postrotate + container_id=$(docker ps -f name=intelowl_nginx --quiet) + docker exec $container_id killall -USR1 nginx + endscript +} \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/manage.sh b/Submodules/IntelOwl/docker/scripts/manage.sh new file mode 100644 index 0000000..923ae30 --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/manage.sh @@ -0,0 +1 @@ +docker exec -ti intelowl_uwsgi python3 manage.py $@ diff --git a/Submodules/IntelOwl/docker/scripts/tail-logs.sh b/Submodules/IntelOwl/docker/scripts/tail-logs.sh new file mode 100644 index 0000000..95355aa --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/tail-logs.sh @@ -0,0 +1,2 @@ +docker exec intelowl_uwsgi ls -al /var/log/intel_owl/$1 +docker exec -ti intelowl_uwsgi tail -f /var/log/intel_owl/$1 \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/scripts/watchman_install.sh b/Submodules/IntelOwl/docker/scripts/watchman_install.sh new file mode 100644 index 0000000..a2e067f --- /dev/null +++ b/Submodules/IntelOwl/docker/scripts/watchman_install.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This script can be disabled during development using WATCHMAN=false env variable +if [ "$WATCHMAN" == "False" ]; then echo "Skipping WATCHMAN installation because we are not in test mode"; exit 0; fi + +pip3 install --compile -r requirements/django-server-requirements.txt + +# install Watchman to enhance performance on the Django development Server +# https://docs.djangoproject.com/en/3.2/ref/django-admin/#runserver +cd /tmp +wget https://github.com/facebook/watchman/releases/download/v2024.05.13.00/watchman-v2024.05.13.00-linux.zip +unzip watchman-*-linux.zip +cd watchman-*-linux/ +mkdir -p /usr/local/{bin,lib} /usr/local/var/run/watchman +cp bin/* /usr/local/bin +cp lib/* /usr/local/lib +chmod 755 /usr/local/bin/watchman +chmod 2777 /usr/local/var/run/watchman +wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb +dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb +rm -rf watchman-*-linux* \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/sqs.override.yml b/Submodules/IntelOwl/docker/sqs.override.yml new file mode 100644 index 0000000..876d2eb --- /dev/null +++ b/Submodules/IntelOwl/docker/sqs.override.yml @@ -0,0 +1,16 @@ +version: "3.8" +services: + uwsgi: + environment: + - AWS_SQS=True + - BROKER_URL=sqs:// + + celery_beat: + environment: + - AWS_SQS=True + - BROKER_URL=sqs:// + + celery_worker_default: + environment: + - AWS_SQS=True + - BROKER_URL=sqs:// diff --git a/Submodules/IntelOwl/docker/test.flower.override.yml b/Submodules/IntelOwl/docker/test.flower.override.yml new file mode 100644 index 0000000..e9ec760 --- /dev/null +++ b/Submodules/IntelOwl/docker/test.flower.override.yml @@ -0,0 +1,7 @@ +version: '3.8' + +services: + flower: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/test.multi-queue.override.yml b/Submodules/IntelOwl/docker/test.multi-queue.override.yml new file mode 100644 index 0000000..d8627a3 --- /dev/null +++ b/Submodules/IntelOwl/docker/test.multi-queue.override.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + celery_worker_local: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + + celery_worker_long: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + + celery_worker_ingestor: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/test.override.yml b/Submodules/IntelOwl/docker/test.override.yml new file mode 100644 index 0000000..0665479 --- /dev/null +++ b/Submodules/IntelOwl/docker/test.override.yml @@ -0,0 +1,52 @@ +version: "3.8" + +services: + uwsgi: + build: + context: .. + dockerfile: docker/Dockerfile + args: + REPO_DOWNLOADER_ENABLED: ${REPO_DOWNLOADER_ENABLED} + WATCHMAN: true + PYCTI_VERSION: ${PYCTI_VERSION:-5.10.0} + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + environment: + - DEBUG=True + - DJANGO_TEST_SERVER=True + - DJANGO_WATCHMAN_TIMEOUT=60 + - WATCHMAN=True + + daphne: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + + nginx: + build: + context: .. + dockerfile: docker/Dockerfile_nginx + image: intelowlproject/intelowl_nginx:test + volumes: + - ../configuration/nginx/django_server.conf:/etc/nginx/conf.d/default.conf + + celery_beat: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + environment: + - DEBUG=True + - DJANGO_TEST_SERVER=True + - DJANGO_WATCHMAN_TIMEOUT=60 + - WATCHMAN=True + + celery_worker_default: + image: intelowlproject/intelowl:test + volumes: + - ../:/opt/deploy/intel_owl + environment: + - DEBUG=True + - DJANGO_TEST_SERVER=True + - DJANGO_WATCHMAN_TIMEOUT=60 + - WATCHMAN=True \ No newline at end of file diff --git a/Submodules/IntelOwl/docker/traefik.override.yml b/Submodules/IntelOwl/docker/traefik.override.yml new file mode 100644 index 0000000..6bbff6d --- /dev/null +++ b/Submodules/IntelOwl/docker/traefik.override.yml @@ -0,0 +1,36 @@ +version: '3.8' + +services: + traefik: + image: "traefik:v2.2" + container_name: "traefik" + command: + #- "--log.level=DEBUG" + - "--api.insecure=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.myresolver.acme.httpchallenge=true" + - "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web" + #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" + - "--certificatesresolvers.myresolver.acme.email=postmaster@example.com" + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" + ports: + - "80:80" + - "443:443" + volumes: + - "./letsencrypt:/letsencrypt" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + + nginx: + depends_on: + - traefik + labels: + - "traefik.enable=true" + - "traefik.http.routers.nginx.rule=Host(`example.com`)" + - "traefik.http.routers.nginx.entrypoints=websecure" + - "traefik.http.routers.nginx.tls.certresolver=myresolver" + ports: + - "443:443" + diff --git a/Submodules/IntelOwl/docs/Makefile b/Submodules/IntelOwl/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/Submodules/IntelOwl/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/Submodules/IntelOwl/docs/make.bat b/Submodules/IntelOwl/docs/make.bat new file mode 100644 index 0000000..6247f7e --- /dev/null +++ b/Submodules/IntelOwl/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/Submodules/IntelOwl/docs/serve_docs.sh b/Submodules/IntelOwl/docs/serve_docs.sh new file mode 100644 index 0000000..229d00e --- /dev/null +++ b/Submodules/IntelOwl/docs/serve_docs.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +export DJANGO_SETTINGS_MODULE="intel_owl.settings" +make html +cd build/html && python3 -m http.server 6969 && cd ../../ \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Advanced-Configuration/index.html b/Submodules/IntelOwl/docs/source/Advanced-Configuration/index.html new file mode 100644 index 0000000..5c984c9 --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Advanced-Configuration/index.html @@ -0,0 +1,1043 @@ + + + + + + + + + + + +Advanced Configuration - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Advanced Configuration

+

This page includes details about some advanced features that Intel Owl provides which can be optionally configured by the administrator.

+ +

ElasticSearch

+

Right now only ElasticSearch v8 is supported.

+

DSL

+

IntelOwl makes use of django-elasticsearch-dsl to index Job results into elasticsearch. The save and delete operations are auto-synced so you always have the latest data in ES.

+

In the env_file_app_template, you'd see various elasticsearch related environment variables. The user should spin their own Elastic Search instance and configure these variables.

+

Kibana

+

Intel Owl provides a Kibana's "Saved Object" configuration (with example dashboard and visualizations). It can be downloaded from here and can be imported into Kibana by going to the "Saved Objects" panel (http://localhost:5601/app/management/kibana/objects).

+

Example Configuration

+
    +
  1. Setup Elastic Search and Kibana and say it is running in a docker service with name elasticsearch on port 9200 which is exposed to the shared docker network. + (Alternatively, you can spin up a local Elastic Search instance, by appending --elastic to the ./start command. Note that the local Elastic Search instance consumes large amount of memory, and hence having >=16GB is recommended.))
  2. +
  3. In the env_file_app, we set ELASTICSEARCH_DSL_ENABLED to True and ELASTICSEARCH_DSL_HOST to elasticsearch:9200.
  4. +
  5. Now start the docker containers and execute
  6. +
+
docker exec -ti intelowl_uwsgi python manage.py search_index --rebuild
+
+

This will build and populate all existing job objects into the jobs index.

+

Business Intelligence

+

IntelOwl makes use of elasticsearch-py to store data that can be used for Business Intelligence purpose. +Since plugin reports are deleted periodically, this feature allows to save indefinitely small amount of data to keep track of how analyzers perform and user usage. +At the moment, the following information are sent to elastic: +- application name +- timestamp +- username +- configuration used +- process_time +- status +- end_time +- parameters

+

Documents are saved in the ELEASTICSEARCH_BI_INDEX-%YEAR-%MONTH, allowing to manage the retention accordingly. +To activate this feature, it is necessary to set ELASTICSEARCH_BI_ENABLED to True in the env_file_app and +ELASTICSEARCH_BI_HOST to elasticsearch:9200 +or your elasticsearch server.

+

An index template is created after the first bulk submission of reports. +If you want to use kibana to visualize your data/make dashboard, you must create an index pattern: +Go to Kibana -> Discover -> Stack Management -> Index Patterns -> search for your index and use as time field timestamp

+

Authentication options

+

IntelOwl provides support for some of the most common authentication methods:

+ +

Google OAuth2

+

The first step is to create a Google Cloud Platform project, and then create OAuth credentials for it.

+

It is important to add the correct callback in the "Authorized redirect URIs" section to allow the application to redirect properly after the successful login. Add this: +

http://<localhost|yourowndomain>/api/auth/google-callback
+

+

After that, specify the client ID and secret as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables and restart IntelOwl to see the applied changes.

+
+

Note

+While configuring Google Auth2 you can choose either to enable access to the all users with a Google Account ("External" mode) or to enable access to only the users of your organization ("Internal" mode). +Reference +
+

LDAP

+

IntelOwl leverages Django-auth-ldap to perform authentication via LDAP.

+

How to configure and enable LDAP on Intel Owl?

+
    +
  1. Change the values with your LDAP configuration inside configuration/ldap_config.py. This file is mounted as a docker volume, so you won't need to rebuild the image.
  2. +
+
+

Note

+For more details on how to configure this file, check the official documentation of the django-auth-ldap library. +
+
    +
  1. Once you have done that, you have to set the environment variable LDAP_ENABLED as True in the environment configuration file env_file_app. + Finally, you can restart the application with docker-compose up
  2. +
+

RADIUS Authentication

+

IntelOwl leverages Django-radius to perform authentication +via RADIUS server.

+

How to configure and enable RADIUS authentication on Intel Owl?

+
    +
  1. Change the values with your RADIUS auth configuration inside configuration/radius_config.py. This file is mounted as a + docker volume, so you won't need to rebuild the image.
  2. +
+
+

Note

+For more details on how to configure this file, check the official documentation of the django-radius library. +
+
    +
  1. Once you have done that, you have to set the environment variable RADIUS_AUTH_ENABLED as True in the environment + configuration file env_file_app. Finally, you can restart the application with docker-compose up
  2. +
+

OpenCTI

+

Like many other integrations that we have, we have an Analyzer and a Connector for the OpenCTI platform.

+

This allows the users to leverage these 2 popular open source projects and frameworks together.

+

So why we have a section here? This is because there are various compatibility problems with the official PyCTI library.

+

We found out (see issues in IntelOwl and PyCTI) that, most of the times, it is required that the OpenCTI version of the server you are using and the pycti version installed in IntelOwl must match perfectly.

+

Because of that, we decided to provide to the users the chance to customize the version of PyCTI installed in IntelOwl based on the OpenCTI version that they are using.

+

To do that, you would need to leverage the option --pycti-version provided by the ./start helper: +* check the default version that would be installed by checking the description of the option --pycti-version with ./start -h +* if the default version is different from your OpenCTI server version, you need to rebuild the IntelOwl Image with ./start test build --pycti-version <your_version> +* then restart the project ./start test up -- --build +* enjoy

+

Cloud Support

+

AWS support

+

We have support for several AWS services.

+

You can customize the AWS Region location of you services by changing the environment variable AWS_REGION. Default is eu-central-1

+

You have to add some credentials for AWS: if you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials: +to allow that just set AWS_IAM_ACCESS to True. If that is not the case, you have to set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

+

S3

+

If you prefer to use S3 to store the analyzed samples, instead of the local storage, you can do it.

+

First, you need to configure the environment variable LOCAL_STORAGE to False to enable it and set AWS_STORAGE_BUCKET_NAME to the AWS bucket you want to use.

+

Then you need to configure permission access to the chosen S3 bucket.

+

Message Broker

+

IntelOwl at the moment supports 3 different message brokers: +- Redis (default) +- RabbitMQ +- Aws SQS

+

The default broker, if nothing is specified, is Redis.

+

To use RabbitMQ, you must use the option --rabbitmq when launching IntelOwl with the ./start script.

+

To use Aws SQS, you must use the option --sqs when launching IntelOwl with the .start script. +In that case, you should create new SQS queues in AWS called intelowl-<environment>-<queue_name> and give your instances on AWS the proper permissions to access it. +Moreover, you must populate the AWS_USER_NUMBER. This is required to connect in the right way to the selected SQS queues. +Only FIFO queues are supported.

+

If you want to use a remote message broker (like an ElasticCache or AmazonMQ instance), you must populate the BROKER_URL environment variable.

+

It is possible to use task priority inside IntelOwl: each User has default priority of 10, and robots users (like the Ingestors) have a priority of 7.
+You can customize these priorities inside Django Admin, in the Authentication.User Profiles section.

+

Websockets

+

Redis is used for two different functions: +- message broker +- websockets

+

For this reason, a Redis instance is mandatory. +You can personalize IntelOwl in two different way: +- with a local Redis instance.

+

This is the default behaviour.

+
    +
  • With a remote Redis instance.
  • +
+

You must use the option --use-external-redis when launching IntelOwl with the .start script. +Moreover, you need to populate the WEBSOCKETS_URL environment variable. If you are using Redis as a message broker too, remember to populate the BROKER_URL environment variable

+

RDS

+

If you like, you could use AWS RDS instead of PostgreSQL for your database. In that case, you should change the database required options accordingly: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD and setup your machine to access the service.

+

If you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials to access the Postgres DB. +To allow that just set AWS_RDS_IAM_ROLE to True. In this case DB_PASSWORD is not required anymore.

+

Moreover, to avoid to run PostgreSQL locally, you would need to use the option --use-external-database when launching IntelOwl with the ./start script.

+

SES

+

If you like, you could use Amazon SES for sending automated emails (password resets / registration requests, etc).

+

You need to configure the environment variable AWS_SES to True to enable it.

+

Secrets

+

You can use the "Secrets Manager" to store your credentials. In this way your secrets would be better protected.

+

Instead of adding the variables to the environment file, you should just add them with the same name on the AWS Secrets Manager and Intel Owl will fetch them transparently.

+

Obviously, you should have created and managed the permissions in AWS in advance and accordingly to your infrastructure requirements.

+

Also, you need to set the environment variable AWS_SECRETS to True to enable this mode.

+

NFS

+

You can use a Network File System for the shared_files that are downloaded runtime by IntelOwl (for example Yara rules).

+

To use this feature, you would need to add the address of the remote file system inside the .env file, +and you would need to use the option --nfs when launching IntelOwl with the ./start script.

+

Google Kubernetes Engine

+

Right now there is no official support for Kubernetes deployments.

+

But we have an active community. Please refer to the following blog post for an example on how to deploy IntelOwl on Google Kubernetes Engine:

+

Deploying Intel-Owl on GKE by Mayank Malik.

+

Queues

+

Multi Queue

+

IntelOwl provides an additional multi-queue.override.yml compose file allowing IntelOwl users to better scale with the performance of their own architecture.

+

If you want to leverage it, you should add the option --multi-queue when starting the project. Example:

+
./start prod up --multi-queue
+
+

This functionality is not enabled by default because this deployment would start 2 more containers so the resource consumption is higher. We suggest to use this option only when leveraging IntelOwl massively.

+

Queue Customization

+

It is possible to define new celery workers: each requires the addition of a new container in the docker-compose file, as shown in the multi-queue.override.yml.

+

Moreover IntelOwl requires that the name of the workers are provided in the docker-compose file. This is done through the environment variable CELERY_QUEUES inside the uwsgi container. Each queue must be separated using the character ,, as shown in the example.

+

One can customize what analyzer should use what queue by specifying so in the analyzer entry in the analyzer_config.json configuration file. If no queue(s) are provided, the default queue will be selected.

+

Queue monitoring

+

IntelOwl provides an additional flower.override.yml compose file allowing IntelOwl users to use Flower features to monitor and manage queues and tasks

+

If you want to leverage it, you should add the option --flower when starting the project. Example:

+
./start prod up --flower
+
+

The flower interface is available at port 5555: to set the credentials for its access, update the environment variables

+
FLOWER_USER
+FLOWER_PWD
+
+

or change the .htpasswd file that is created in the docker directory in the intelowl_flower container.

+

Manual Usage

+

The ./start script essentially acts as a wrapper over Docker Compose, performing additional checks. +IntelOwl can still be started by using the standard docker compose command, but all the dependencies have to be manually installed by the user.

+

Options

+

The --project-directory and -p options are required to run the project. +Default values set by ./start script are "docker" and "intel_owl", respectively.

+

The startup is based on chaining various Docker Compose YAML files using -f option. +All Docker Compose files are stored in docker/ directory of the project. +The default compose file, named default.yml, requires configuration for an external database and message broker. +In their absence, the postgres.override.yml and rabbitmq.override.yml files should be chained to the default one.

+

The command composed, considering what is said above (using sudo), is +

sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -p intel_owl up
+

+

The other most common compose file that can be used is for the testing environment. +The equivalent of running ./start test up is adding the test.override.yml file, resulting in: +

sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -f docker/test.override.yml -p intel_owl up
+

+

All other options available in the ./start script (./start -h to view them) essentially chain other compose file to docker compose command with corresponding filenames.

+

Optional Analyzer

+

IntelOwl includes integrations with some analyzer that are not enabled by default. +These analyzers, stored under the integrations/ directory, are packed within Docker Compose files. +The compose.yml file has to be chained to include the analyzer. +The additional compose-test.yml file has to be chained for testing environment.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Advanced-Usage/index.html b/Submodules/IntelOwl/docs/source/Advanced-Usage/index.html new file mode 100644 index 0000000..0a190ed --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Advanced-Usage/index.html @@ -0,0 +1,945 @@ + + + + + + + + + + + +Advanced Usage - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Advanced Usage

+

This page includes details about some advanced features that Intel Owl provides which can be optionally enabled. Namely,

+ +

Organizations and User management

+

Starting from IntelOwl v4, a new "Organization" section is available on the GUI. This section substitute the previous permission management via Django Admin and aims to provide an easier way to manage users and visibility.

+

Multi Tenancy

+

Thanks to the "Organization" feature, IntelOwl can be used by multiple SOCs, companies, etc...very easily. +Right now it works very simply: only users in the same organization can see analysis of one another. An user can belong to an organization only.

+

Manage organizations

+

You can create a new organization by going to the "Organization" section, available under the Dropdown menu you cand find under the username.

+

Once you create an organization, you are the unique "Owner" of that organization. So you are the only one who can delete the organization and promote/demote/kick users. +Another role, which is called "Admin", can be set to a user (via the Django Admin interface only for now). +Owners and admins share the following powers: they can manage invitations and the organization's plugin configuration.

+

Accept Invites

+

Once an invite has sent, the invited user has to login, go to the "Organization" section and accept the invite there. Afterwards the Administrator will be able to see the user in his "Organization" section.

+

img.png

+

Plugins Params and Secrets

+

From IntelOwl v4.1.0, Plugin Parameters and Secrets can be defined at the organization level, in the dedicated section. +This allows to share configurations between users of the same org while allowing complete multi-tenancy of the application. +Only Owners and Admins of the organization can set, change and delete them.

+

Disable Plugins at Org level

+

The org admin can disable a specific plugin for all the users in a specific org. +To do that, Org Admins needs to go in the "Plugins" section and click the button "Enabled for organization" of the plugin that they want to disable.

+

img.png

+

Registration

+

Since IntelOwl v4.2.0 we added a Registration Page that can be used to manage Registration requests when providing IntelOwl as a Service.

+

After a user registration has been made, an email is sent to the user to verify their email address. If necessary, there are buttons on the login page to resend the verification email and to reset the password.

+

Once the user has verified their email, they would be manually vetted before being allowed to use the IntelOwl platform. The registration requests would be handled in the Django Admin page by admins. +If you have IntelOwl deployed on an AWS instance with an IAM role you can use the SES service.

+

To have the "Registration" page to work correctly, you must configure some variables before starting IntelOwl. See Optional Environment Configuration

+

In a development environment the emails that would be sent are written to the standard output.

+

Recaptcha configuration

+

The Registration Page contains a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown.

+

If your intention is to publish IntelOwl as a Service you should first remember to comply to the AGPL License.

+

Then you need to add the generated Recaptcha Secret in the RECAPTCHA_SECRET_KEY value in the env_file_app file.

+

Afterwards you should configure the Recaptcha Key for your site and add that value in the RECAPTCHA_SITEKEY in the frontend/public/env.js file. +In that case, you would need to re-build the application to have the changes properly reflected.

+

Optional Analyzers

+

Some analyzers which run in their own Docker containers are kept disabled by default. They are disabled by default to prevent accidentally starting too many containers and making your computer unresponsive.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameAnalyzersDescription
Malware Tools Analyzers +
    +
  • PEframe_Scan
  • +
  • Capa_Info
  • +
  • Floss
  • +
  • Strings_Info
  • +
  • ClamAV
  • +
  • APKiD
  • +
  • Thug_URL_Info, + Thug_HTML_Info
  • +
  • BoxJS
  • +
  • Qiling_Windows, + Qiling_Windows_Shellcode, + Qiling_Linux, + Qiling_Linux_Shellcode
  • +
+
+
    +
  • PEFrame performs static analysis on Portable Executable malware and malicious MS Office documents
  • +
  • Capa detects capabilities in executable files
  • +
  • FLOSS automatically deobfuscate strings from malware binaries
  • +
  • String_Info_Classic extracts human-readable strings where as ML version of it ranks them
  • +
  • ClamAV antivirus engine scans files for trojans, viruses, malwares using a multi-threaded daemon
  • +
  • APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.
  • +
  • Thug performs hybrid dynamic/static analysis on a URL or HTML page.
  • +
  • Box-JS is a tool for studying JavaScript malware
  • +
  • Qiling is a tool for emulating the execution of a binary file or a shellcode. + It requires the configuration of its rootfs, and the optional configuration of profiles. + The rootfs can be copied from the Qiling project: please remember that Windows dll must be manually added for license reasons. + Qiling provides a DllCollector to retrieve dlls from your licensed Windows. + Profiles must be placed in the profiles subfolder +
  • +
+
TOR AnalyzersOnionscanScans TOR .onion domains for privacy leaks and information disclosures.
CyberChefCyberChefRun a transformation on a CyberChef server using pre-defined or custom recipes(rules that describe how the input has to be transformed). Check further instructions here
PCAP AnalyzersSuricataYou can upload a PCAP to have it analyzed by Suricata with the open Ruleset. The result will provide a list of the triggered signatures plus a more detailed report with all the raw data generated by Suricata. You can also add your own rules (See paragraph "Analyzers with special configuration"). The installation is optimized for scaling so the execution time is really fast.
PhoneInfogaPhoneInfoga_scanPhoneInfoga is one of the most advanced tools to scan international phone numbers. It allows you to first gather basic information such as country, area, carrier and line type, then use various techniques to try to find the VoIP provider or identify the owner. It works with a collection of scanners that must be configured in order for the tool to be effective. PhoneInfoga doesn't automate everything, it's just there to help investigating on phone numbers. here
+

To enable all the optional analyzers you can add the option --all_analyzers when starting the project. Example:

+
./start prod up --all_analyzers
+
+

Otherwise you can enable just one of the cited integration by using the related option. Example:

+
./start prod up --tor_analyzers
+
+

Customize analyzer execution

+

Some analyzers provide the chance to customize the performed analysis based on parameters that are different for each analyzer.

+
from the GUI
+

You can click on "Runtime Configuration" img.png button in the "Scan" page and add the runtime configuration in the form of a dictionary. +Example:

+
"VirusTotal_v3_File": {
+    "force_active_scan_if_old": true
+}
+
+
from Pyintelowl
+

While using send_observable_analysis_request or send_file_analysis_request endpoints, you can pass the parameter runtime_configuration with the optional values. +Example:

+
runtime_configuration = {
+    "Doc_Info": {
+        "additional_passwords_to_check": ["passwd", "2020"]
+    }
+}
+pyintelowl_client.send_file_analysis_request(..., runtime_configuration=runtime_configuration)
+
+

PhoneInfoga

+

PhoneInfoga provides several Scanners to extract as much information as possible from a given phone number. Those scanners may require authentication, so they're automatically skipped when no authentication credentials are found.

+

By default the scanner used is local. +Go through this guide to initiate other required API keys related to this analyzer.

+

CyberChef

+

You can either use pre-defined recipes or create your own as +explained here.

+

To use a pre-defined recipe, set the predefined_recipe_name argument to the name of the recipe as +defined here. Else, leave the predefined_recipe_name argument empty and set +the custom_recipe argument to the contents of +the recipe you want to +use.

+

Additionally, you can also (optionally) set the output_type argument.

+
Pre-defined recipes
+
    +
  • "to decimal": [{"op": "To Decimal", "args": ["Space", False]}]
  • +
+

Analyzers with special configuration

+

Some analyzers could require a special configuration:

+
    +
  • +

    GoogleWebRisk: this analyzer needs a service account key with the Google Cloud credentials to work properly. + You should follow the official guide for creating the key. + Then you can populate the secret service_account_json for that analyzer with the JSON of the service account file.

    +
  • +
  • +

    ClamAV: this Docker-based analyzer uses clamd daemon as its scanner and is communicating with clamdscan utility to scan files. The daemon requires 2 different configuration files: clamd.conf(daemon's config) and freshclam.conf (virus database updater's config). These files are mounted as docker volumes in /integrations/malware_tools_analyzers/clamav and hence, can be edited by the user as per needs, without restarting the application. Moreover ClamAV is integrated with unofficial open source signatures extracted with Fangfrisch. The configuration file fangfrisch.conf is mounted in the same directory and can be customized on your wish. For instance, you should change it if you want to integrate open source signatures from SecuriteInfo

    +
  • +
  • +

    Suricata: you can customize the behavior of Suricata:

    +
  • +
  • +

    /integrations/pcap_analyzers/config/suricata/rules: here there are Suricata rules. You can change the custom.rules files to add your own rules at any time. Once you made this change, you need to either restart IntelOwl or (this is faster) run a new analysis with the Suricata analyzer and set the parameter reload_rules to true.

    +
  • +
  • +

    /integrations/pcap_analyzers/config/suricata/etc: here there are Suricata configuration files. Change it based on your wish. Restart IntelOwl to see the changes applied.

    +
  • +
  • +

    Yara:

    +
  • +
  • You can customize both the repositories parameter and private_repositories secret to download and use different rules from the default that IntelOwl currently support.
      +
    • The repositories values is what will be used to actually run the analysis: if you have added private repositories, remember to add the url in repositories too!
    • +
    +
  • +
  • You can add local rules inside the directory at /opt/deploy/files_required/yara/YOUR_USERNAME/custom_rules/. Please remember that these rules are not synced in a cluster deploy: for this reason is advised to upload them on GitHub and use the repositories or private_repositories attributes.
  • +
+

Notifications

+

Since v4, IntelOwl integrated the notification system from the certego_saas package, allowing the admins to create notification that every user will be able to see.

+

The user would find the Notifications button on the top right of the page:

+

+

There the user can read notifications provided by either the administrators or the IntelOwl Maintainers.

+

As an Admin, if you want to add a notification to have it sent to all the users, you have to login to the Django Admin interface, go to the "Notifications" section and add it there. +While adding a new notification, in the body section it is possible to even use HTML syntax, allowing to embed images, links, etc; +in the app_name field, please remember to use intelowl as the app name.

+

Everytime a new release is installed, once the backend goes up it will automatically create a new notification, +having as content the latest changes described in the CHANGELOG.md, +allowing the users to keep track of the changes inside intelowl itself.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Contribute/index.html b/Submodules/IntelOwl/docs/source/Contribute/index.html new file mode 100644 index 0000000..da15da8 --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Contribute/index.html @@ -0,0 +1,1508 @@ + + + + + + + + + + + +Contribute - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Contribute

+

There are a lot of different ways you could choose to contribute to the IntelOwl Project:

+ +

Rules

+

Intel Owl welcomes contributors from anywhere and from any kind of education or skill level. We strive to create a community of developers that is welcoming, friendly and right.

+

For this reason it is important to follow some easy rules based on a simple but important concept: Respect.

+
    +
  • +

    Before asking any questions regarding how the project works, please read through all the documentation and install the project on your own local machine to try it and understand how it basically works. This is a form of respect to the maintainers.

    +
  • +
  • +

    DO NOT contact the maintainers with direct messages unless it is an urgent request. We don't have much time and cannot just answer to all the questions that we receive like "Guide me please! Help me understand how the project work". There is plenty of documentation and a lot of people in the community that can help you and would benefit from your questions. Share your problems and your knowledge. Please ask your questions in open channels (Github and Slack). This is a form of respect to the maintainers and to the community.

    +
  • +
  • +

    Before starting to work on an issue, you need to get the approval of one of the maintainers. Therefore please ask to be assigned to an issue. If you do not that but you still raise a PR for that issue, your PR can be rejected. This is a form of respect for both the maintainers and the other contributors who could have already started to work on the same problem.

    +
  • +
  • +

    When you ask to be assigned to an issue, it means that you are ready to work on it. When you get assigned, take the lock and then you disappear, you are not respecting the maintainers and the other contributors who could be able to work on that. So, after having been assigned, you have a week of time to deliver your first draft PR. After that time has passed without any notice, you will be unassigned.

    +
  • +
  • +

    Once you started working on an issue and you have some work to share and discuss with us, please raise a draft PR early with incomplete changes. This way you can continue working on the same and we can track your progress and actively review and help. This is a form of respect to you and to the maintainers.

    +
  • +
  • +

    When creating a PR, please read through the sections that you will find in the PR template and compile it appropriately. If you do not, your PR can be rejected. This is a form of respect to the maintainers.

    +
  • +
+

Code Style

+

Keeping to a consistent code style throughout the project makes it easier to contribute and collaborate. We make use of psf/black and isort for code formatting and flake8 for style guides.

+

How to start (Setup project and development instance)

+

This guide assumes that you have already performed the steps required to install the project. If not, please do it (Installation Guide).

+

Create a personal fork of the project on Github. +Then, please create a new branch based on the develop branch that contains the most recent changes. This is mandatory.

+

git checkout -b myfeature develop

+

Then we strongly suggest to configure pre-commit to force linters on every commits you perform

+
# From the project directory
+python3 -m venv venv
+source venv/bin/activate
+# from the project base directory
+pip install pre-commit
+pre-commit install
+
+# create .env file for controlling repo_downloader.sh 
+# (to speed up image builds during development: it avoid downloading some repos)
+cp docker/.env.start.test.template docker/.env.start.test
+
+# set STAGE env variable to "local"
+sed -i "s/STAGE=\"production\"/STAGE=\"local\"/g" docker/env_file_app
+
+

Backend

+

Now, you can execute IntelOwl in development mode by selecting the mode test while launching the startup script:

+
./start test up
+
+

Every time you perform a change, you should perform an operation to reflect the changes into the application:

+
    +
  • if you changed the python requirements, restart the application and re-build the images. This is the slowest process. You can always choose this way but it would waste a lot of time.
  • +
+
./start test down && ./start test up -- --build
+
+
    +
  • if you changed either analyzers, connectors, playbooks or anything that is executed asynchronously by the "celery" containers, you just need to restart the application because we leverage Docker bind volumes that will reflect the changes to the containers. This saves the time of the build
  • +
+
./start test down && ./start test up
+
+
    +
  • if you made changes to either the API or anything that is executed only by the application server, changes will be instantly reflected and you don't need to do anything. This is thanks to the Django Development server that is executed instead of uwsgi while using the test mode
  • +
+

NOTE about documentation:

+

If you made any changes to an existing model/serializer/view, please run the following command to generate a new version of the API schema and docs:

+
docker exec -it intelowl_uwsgi python manage.py spectacular --file docs/source/schema.yml && make html
+
+

Frontend

+

To start the frontend in "develop" mode, you can execute the startup npm script within the folder frontend:

+
cd frontend/
+# Install
+npm i
+# Start
+DANGEROUSLY_DISABLE_HOST_CHECK=true npm start
+# See https://create-react-app.dev/docs/proxying-api-requests-in-development/#invalid-host-header-errors-after-configuring-proxy for why we use that flag in development mode
+
+

Most of the time you would need to test the changes you made together with the backend. In that case, you would need to run the backend locally too:

+
./start prod up
+
+
+

Note

+
    +
  • Running prod would be faster because you would leverage the official images and you won't need to build the backend locally. In case you would need to test backend changes too at the same time, please use test and refer to the previous section of the documentation.
  • +
  • This works thanks to the directive proxy in the frontend/package.json configuration
  • +
  • It may happen that the backend build does not work due to incompatibility between the frontend version you are testing with the current complete IntelOwl version you are running. In those cases, considering that you don't need to build the frontend together with the backend because you are already testing it separately, we suggest to remove the first build step (the frontend part) from the main Dockerfile temporarily and build IntelOwl with only the backend. In this way there won't be conflict issues.
  • +
+
+

Certego-UI

+

The IntelOwl Frontend is tightly linked to the certego-ui library. Most of the React components are imported from there. Because of this, it may happen that, during development, you would need to work on that library too. +To install the certego-ui library, please take a look to npm link and remember to start certego-ui without installing peer dependencies (to avoid conflicts with IntelOwl dependencies):

+
git clone https://github.com/certego/certego-ui.git
+# change directory to the folder where you have the cloned the library
+cd certego-ui/
+# install, without peer deps (to use packages of IntelOwl)
+npm i --legacy-peer-deps
+# create link to the project (this will globally install this package)
+sudo npm link
+# compile the library
+npm start
+
+

Then, open another command line tab, create a link in the frontend to the certego-ui and re-install and re-start the frontend application (see previous section):

+
cd frontend/
+npm link @certego/certego-ui
+
+

This trick will allow you to see reflected every changes you make in the certego-ui directly in the running frontend application.

+
Example application
+

The certego-ui application comes with an example project that showcases the components that you can re-use and import to other projects, like IntelOwl:

+
# To have the Example application working correctly, be sure to have installed `certego-ui` *without* the `--legacy-peer-deps` option and having it started in another command line
+cd certego-ui/
+npm i
+npm start
+# go to another tab
+cd certego-ui/example/
+npm i
+npm start
+
+

How to add a new Plugin

+

IntelOwl was designed to ease the addition of new plugins. With a simple python script you can integrate your own engine or integrate an external service in a short time.

+

There are two possible cases: +1. You are creating an entirely new Plugin, meaning that you actually wrote python code +2. You are creating a new Configuration for some code that already exists.

+

If you are doing the step number 2, you can skip this paragraph.

+

First, you need to create the python code that will be actually executed. You can easily take other plugins as example to write this. +Then, you have to create a Python Module model. You can do this in the Django Admin page: +You have to specify which type of Plugin you wrote, and its python module. Again, you can use as an example an already configured Python Module.

+

Some Python Module requires to update some part of its code in a schedule way: for example Yara requires to update the rule repositories, QuarkEngine to update its database and so on. +If the Python Module that you define need this type of behaviour, you have to configure two things: +- In the python code, you have to override a method called update and put the updating logic (see other plugins for examples) there. +- In the model class, you have to add the update_schedule (crontab syntax) that define when the update should be executed.

+

Some Python Module requires further check to see if the service provider is able to answer requests; for example if you have done too many requests, or the website is currently down for maintenance and so on. +If the Python Module that you define need this type of behaviour, you have to configure two things: +- In the python code, you can override a method called health_check and put there the custom health check logic. As default, plugins will try to make an HTTP HEAD request to the configured url (the Plugin must have a url attribute). +- In the model class, you have to add the health_check_schedule (crontab syntax) that define when the health check should be executed.

+

Press Save and continue editing to, at the moment, manually ad the Parameters that the python code requires (the class attributes that you needed): + 1. *name: Name of the parameter that will be dynamically added to the python class (if is a secret, in the python code a _ wil be prepended to the name) + 2. *type: data type, string, list, dict, integer, boolean, float + 3. *description + 4. *required: true or false, meaning that a value is necessary to allow the run of the analyzer + 5. *is_secret: true or false

+

At this point, you can follow the specific guide for each plugin

+

How to add a new Analyzer

+

You may want to look at a few existing examples to start to build a new one, such as:

+
    +
  • shodan.py, if you are creating an observable analyzer
  • +
  • malpedia_scan.py, if you are creating a file analyzer
  • +
  • peframe.py, if you are creating a docker based analyzer
  • +
  • Please note: If the new analyzer that you are adding is free for the user to use, please add it in the FREE_TO_USE_ANALYZERS playbook. To do this you have to make a migration file; you can use 0026_add_mmdb_analyzer_free_to_use as a template.
  • +
+

After having written the new python module, you have to remember to:

+
    +
  1. Put the module in the file_analyzers or observable_analyzers directory based on what it can analyze
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new analyzer. This is a trick to have tests in the same class of its analyzer.
  4. +
  5. Create the configuration inside django admin in Analyzers_manager/AnalyzerConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. *Type: observable or file
  18. +
  19. *Docker based: if the analyzer run through a docker instance
  20. +
  21. *Maximum tlp: maximum tlp to allow the run on the connector
  22. +
  23. ~Observable supported: required if type is observable
  24. +
  25. ~Supported filetypes: required if type is file and not supported filetypes is empty
  26. +
  27. Run hash: if the analyzer supports hash as inputs
  28. +
  29. ~Run hash type: required if run hash is True
  30. +
  31. ~Not supported filetypes: required if type is file and supported filetypes is empty
  32. +
+

Integrating a docker based analyzer

+

If the analyzer you wish to integrate doesn't exist as a public API or python package, it should be integrated with its own docker image +which can be queried from the main Django app.

+
    +
  • It should follow the same design principle as the other such existing integrations, unless there's very good reason not to.
  • +
  • The dockerfile should be placed at ./integrations/<analyzer_name>/Dockerfile.
  • +
  • Two docker-compose files compose.yml for production and compose-tests.yml for testing should be placed under ./integrations/<analyzer_name>.
  • +
  • If your docker-image uses any environment variables, add them in the docker/env_file_integrations_template.
  • +
  • Rest of the steps remain same as given under "How to add a new analyzer".
  • +
+

How to add a new Connector

+

You may want to look at a few existing examples to start to build a new one:

+ +

After having written the new python module, you have to remember to:

+
    +
  1. Put the module in the connectors directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new connector. This is a trick to have tests in the same class of its connector.
  4. +
  5. Create the configuration inside django admin in Connectors_manager/ConnectorConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. *Maximum tlp: maximum tlp to allow the run on the connector
  18. +
  19. *Run on failure: if the connector should be run even if the job fails
  20. +
+

How to add a new Ingestor

+
    +
  1. Put the module in the ingestors directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new ingestor. This is a trick to have tests in the same class of its ingestor.
  4. +
  5. Create the configuration inside django admin in Ingestors_manager/IngestorConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. Playbook to Execute: Playbook that *will** be executed on every IOC retrieved
  18. +
  19. *Schedule: Crontab object that describes the schedule of the ingestor. You are able to create a new clicking the plus symbol.
  20. +
+

How to add a new Pivot

+
    +
  1. Put the module in the pivots directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new pivot. This is a trick to have tests in the same class of its pivot.
  4. +
  5. Create the configuration inside django admin in Pivots_manager/PivotConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Routing key: celery queue that will be used
  14. +
  15. *Soft_time_limit: maximum time for the task execution
  16. +
  17. Playbook to Execute: Playbook that *will** be executed in the Job generated by the Pivot
  18. +
+

Most of the times you don't need to create a new Pivot Module. There are already some base modules that can be extended. +The most important ones are the following 2: +1.AnyCompare: use this module if you want to create a custom Pivot from a specific value extracted from the results of the analyzers/connectors. How? you should populate the parameter field_to_compare with the dotted path to the field you would like to extract the value from. +2.SelfAnalyzable: use this module if you want to create a custom Pivot that would analyze again the same observable/file.

+

How to add a new Visualizer

+

Configuration

+
    +
  1. Put the module in the visualizers directory
  2. +
  3. Remember to use _monkeypatch() in its class to create automated tests for the new visualizer. This is a trick to have tests in the same class of its visualizer.
  4. +
  5. Create the configuration inside django admin in Visualizers_manager/VisualizerConfigs (* = mandatory, ~ = mandatory on conditions)
  6. +
  7. *Name: specific name of the configuration
  8. +
  9. *Python module: .
  10. +
  11. *Description: description of the configuration
  12. +
  13. *Config:
      +
    1. *Queue: celery queue that will be used
    2. +
    3. *Soft_time_limit: maximum time for the task execution
    4. +
    +
  14. +
  15. Playbook: Playbook that *must** have run to execute the visualizer
  16. +
+

Python class

+

The visualizers' python code could be not immediate, so a small digression on how it works is necessary. +Visualizers have as goal to create a data structure inside the Report that the frontend is able to parse and correctly visualize on the page. +To do so, some utility classes have been made:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ClassDescriptionVisual representation/example
VisualizablePageA single page of the final report, made of different levels. Each page added is represented as a new tab in frontend.Visualizable Page example
VisualizableLevel + Each level corresponds to a line in the final frontend visualizations. Every level is made of a + VisualizableHorizontalList. + The dimension of the level can be customized with the size parameter (1 is the biggest, 6 is the smallest). + Visualizable Level example
VisualizableHorizontalListAn horizontal list of visualizable elements. In the example there is an horizontal list of vertical lists.Visualizable Horizontal List Example
VisualizableVerticalListA vertical list made of a name, a title, and the list of elements.Visualizable Vertical List Example
VisualizableTableA table of visualizable elements. In the example there is a table of base and vertical lists.Visualizable Table Example
VisualizableBoolThe representation of a boolean value. It can be enabled or disabled with colors.Visualizable Bool example
VisualizableTitleThe representation of a tuple, composed of a title and a value.Visualizable Title example
VisualizableBaseThe representation of a base string. Can have a link attached to it and even an icon. The background color can be changed.The title above is composed by two `VisualizableBase`
+

Inside a Visualizer you can retrieve the reports of the analyzers and connectors that have been specified inside configuration of the Visualizer itself using .analyzer_reports() and .connector_reports(). +At this point, you can compose these values as you wish wrapping them with the Visualizable classes mentioned before.

+

The best way to create a visualizer is to define several methods, one for each Visualizable you want to show in the UI, in your new visualizer and decore them with visualizable_error_handler_with_params. This decorator handles exceptions: in case there is a bug during the generation of a Visualizable element, it will be show an error instead of this component and all the other Visualizable are safe and will render correctly. Be careful using it because is a function returning a decorator! This means you need to use a syntax like this: +

@visualizable_error_handler_with_params(error_name="custom visualizable", error_size=VisualizableSize.S_2)
+def custom_visualizable(self):
+   ...
+

+

instead of the syntax of other decorators that doesn't need the function call.

+

You may want to look at a few existing examples to start to build a new one:

+ +

How to share your plugin with the community

+

To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration + 1. You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/YOUR_PLUGIN_manager/migrations). The script will create the following models: + 1. PythonModule + 2. AnalyzerConfig + 3. Parameter + 4. PluginConfig

+
    +
  1. Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin AnalyzerConfig <new_analyzer_name>
  2. +
+

Add the new analyzer in the lists in the docs: Usage. Also, if the analyzer provides additional optional configuration, add the available options here: Advanced-Usage

+

In the Pull Request remember to provide some real world examples (screenshots and raw JSON results) of some successful executions of the analyzer to let us understand how it would work.

+

How to add a new Playbook

+
    +
  1. Create the configuration inside django admin in Playbooks_manager/PlaybookConfigs (* = mandatory, ~ = mandatory on conditions)
  2. +
  3. *Name: specific name of the configuration
  4. +
  5. *Description: description of the configuration
  6. +
  7. *Type: list of types that are supported by the playbook
  8. +
  9. *Analyzers: List of analyzers that will be run
  10. +
  11. *Connectors: List of connectors that will be run
  12. +
+

How to share your playbook with the community

+

To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration +You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/playbook_manager/migrations).

+

Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin PlaybookConfig <new_analyzer_name>

+

How to modify a plugin

+

If the changes that you have to make should stay local, you can just change the configuration inside the Django admin page.

+

But if, instead, you want your changes to be usable by every IntelOwl user, you have to create a new migration.

+

To do so, you can use the following snippets as an example: +1. You have to create a new migration file +2. Add as dependency the previous last migration of the package +3. You have to create a forward and a reverse function +4. You have to make the proper changes of the configuration inside these functions (change parameters, secrets, or even delete the configuration) + 1. If changes are made, you have to validate the instance calling .full_clean() and then you can save the instance with .save()

+

Example: how to add a new parameter in the configuration with a default value

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   PluginConfig = apps.get_model("api_app", "PluginConfig")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   p = Parameter(name="mynewfield", type="str", description="Test field", is_secret=False, required=True, python_module=pm)
+   p.full_clean()
+   p.save()
+   for connector in pm.connectorconfigs.all():
+    pc = PluginConfig(value="test", connector_config=connector, python_module=pm, for_organization=False, owner=None, parameter=p)
+    pc.full_clean()
+    pc.save()
+
+

Example: how to add a new secret in the configuration

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   p = Parameter(name="mynewsecret", type="str", description="Test field", is_secret=True, required=True, python_module=pm)
+   p.full_clean()
+   p.save()
+
+

Example: how to delete a parameter

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   Parameter.objects.get(name="myoldfield", python_module=pm).delete()
+
+

Example: how to change the default value of a parameter

+
def migrate(apps, schema_editor):
+   PythonModule = apps.get_model("api_app", "PythonModule")
+   Parameter = apps.get_model("api_app", "Parameter")
+   PluginConfig = apps.get_model("api_app", "PluginConfig")
+   pm = PythonModule.objects.get(module="test.Test", base_path="api_app.connectors_manager.connectors")
+   p = Parameter.objects.get(name="myfield", python_module=pm)
+   PluginConfig.objects.filter(parameter=p, python_module=pm, for_organization=False, owner=None ).update(value="newvalue")
+
+

Modifying functionalities of the Certego packages

+

Since v4, IntelOwl leverages some packages from Certego:

+
    +
  • certego-saas that integrates some common reusable Django applications and tools that can be used for generic services.
  • +
  • certego-ui that contains reusable React components for the UI.
  • +
+

If you need to modify the behavior or add feature to those packages, please follow the same rules for IntelOwl and request a Pull Request there. The same maintainers of IntelOwl will answer to you.

+

Follow these guides to understand how to start to contribute to them while developing for IntelOwl:

+
    +
  • certego-saas: create a fork, commit your changes in your local repo, then change the commit hash to the last one you made in the requirements file. Ultimately re-build the project
  • +
  • certego-ui: Frontend doc
  • +
+

How to test the application

+

IntelOwl makes use of the django testing framework and the unittest library for unit testing of the API endpoints and End-to-End testing of the analyzers and connectors.

+

Configuration

+
    +
  • +

    In the encrypted folder tests/test_files.zip (password: "intelowl") there are some files that you can use for testing purposes.

    +
  • +
  • +

    With the following environment variables you can customize your tests:

    +
  • +
  • +

    DISABLE_LOGGING_TEST -> disable logging to get a clear output

    +
  • +
  • +

    MOCK_CONNECTIONS -> mock connections to external API to test the analyzers without a real connection or a valid API key

    +
  • +
  • +

    If you prefer to use custom inputs for tests, you can change the following environment variables in the environment file based on the data you would like to test:

    +
  • +
  • TEST_MD5
  • +
  • TEST_URL
  • +
  • TEST_IP
  • +
  • TEST_DOMAIN
  • +
+

Setup containers

+

The point here is to launch the code in your environment and not the last official image in Docker Hub. +For this, use the test or the ci option when launching the containers with the ./start script.

+
    +
  • Use the test option to actually execute tests that simulate a real world environment without mocking connections.
  • +
  • Use the ci option to execute tests in a CI environment where connections are mocked.
  • +
+
$ ./start test up
+$ # which corresponds to the command: docker-compose -f docker/default.yml -f docker/test.override.yml up
+
+

Launch tests

+

Now that the containers are up, we can launch the test suite.

+

Backend

+
Run all tests
+

Examples:

+
$ docker exec intelowl_uwsgi python3 manage.py test
+
+
Run tests for a particular plugin
+

To test a plugin in real environment, i.e. without mocked data, we suggest that you use the GUI of IntelOwl directly. +Meaning that you have your plugin configured, you have selected a correct observable/file to analyze, +and the final report shown in the GUI of IntelOwl is exactly what you wanted.

+
Run tests available in a particular file
+

Examples:

+
$ docker exec intelowl_uwsgi python3 manage.py test tests.api_app tests.test_crons # dotted paths
+
+

Frontend

+

All the frontend tests must be run from the folder frontend. +The tests can contain log messages, you can suppress then with the environment variable SUPPRESS_JEST_LOG=True.

+
Run all tests
+
npm test
+
+
Run a specific component tests
+
npm test -- -t <componentPath>
+// example
+npm test tests/components/auth/Login.test.jsx
+
+
Run a specific test
+
npm test -- -t '<describeString> <testString>'
+// example
+npm test -- -t "Login component User login"
+
+

Create a pull request

+

Remember!!!

+

Please create pull requests only for the branch develop. That code will be pushed to master only on a new release.

+

Also remember to pull the most recent changes available in the develop branch before submitting your PR. If your PR has merge conflicts caused by this behavior, it won't be accepted.

+

Install testing requirements

+

Run pip install -r requirements/test-requirements.txt to install the requirements to validate your code.

+

Pass linting and tests

+
    +
  1. Run psf/black to lint the files automatically, then flake8 to check and isort:
  2. +
+

(if you installed pre-commit this is performed automatically at every commit)

+
$ black . --exclude "migrations|venv"
+$ flake8 . --show-source --statistics
+$ isort . --profile black --filter-files --skip venv
+
+

if flake8 shows any errors, fix them.

+
    +
  1. Run the build and start the app using the docker-compose test file. In this way, you would launch the code in your environment and not the last official image in Docker Hub:
  2. +
+
$ ./start ci build
+$ ./start ci up
+
+
    +
  1. Here, we simulate the GitHub CI tests locally by running the following 3 tests:
  2. +
+
$ docker exec -ti intelowl_uwsgi unzip -P intelowl tests/test_files.zip -d test_files
+$ docker exec -ti intelowl_uwsgi python manage.py test tests
+
+
+

Note: IntelOwl has dynamic testing suite. This means that no explicit analyzers/connector tests are required after the addition of a new analyzer or connector.

+
+

If everything is working, before submitting your pull request, please squash your commits into a single one!

+

How to squash commits to a single one

+
    +
  • Run git rebase -i HEAD~[NUMBER OF COMMITS]
  • +
  • You should see a list of commits, each commit starting with the word "pick".
  • +
  • Make sure the first commit says "pick" and change the rest from "pick" to "squash". -- This will squash each commit into the previous commit, which will continue until every commit is squashed into the first commit.
  • +
  • Save and close the editor.
  • +
  • It will give you the opportunity to change the commit message.
  • +
  • Save and close the editor again.
  • +
  • Then you have to force push the final, squashed commit: git push --force-with-lease origin.
  • +
+

Squashing commits can be a tricky process but once you figure it out, it's really helpful and keeps our repo concise and clean.

+

Debug application problems

+

Keep in mind that, if any errors arise during development, you would need to check the application logs to better understand what is happening so you can easily address the problem.

+

This is the reason why it is important to add tons of logs in the application...if they are not available in time of needs you would cry really a lot.

+

Where are IntelOwl logs? +With a default installation of IntelOwl, you would be able to get the application data from the following paths in your OS: +* /var/lib/docker/volumes/intel_owl_generic_logs/_data/django: Django Application logs +* /var/lib/docker/volumes/intel_owl_generic_logs/_data/uwsgi: Uwsgi application server logs +* /var/lib/docker/volumes/intel_owl_nginx_logs/_data/: Nginx Web Server Logs

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Installation/index.html b/Submodules/IntelOwl/docs/source/Installation/index.html new file mode 100644 index 0000000..275bc6d --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Installation/index.html @@ -0,0 +1,1101 @@ + + + + + + + + + + + +Installation - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

Installation

+

Requirements

+

The project leverages docker compose with a custom Bash script and you need to have the following packages installed in your machine: +* docker - v19.03.0+ +* docker-compose - v2.3.4+

+

In some systems you could find pre-installed older versions. Please check this and install a supported version before attempting the installation. Otherwise it would fail. +Note: We've added a new Bash script initialize.sh that will check compatibility with your system and attempt to install the required dependencies.

+
+

Note

+
    +
  • The project uses public docker images that are available on Docker Hub
  • +
  • IntelOwl is tested and supported to work in a Debian distro. More precisely we suggest using Ubuntu. Other Linux-based OS should work but that has not been tested much. It may also run on Windows, but that is not officially supported.
  • +
  • IntelOwl does not support ARM at the moment. We'll fix this with the next v6.0.5 release +
  • Before installing remember that you must comply with the LICENSE and the Legal Terms
  • +
+
+
+

Warning

+The start script requires a `bash` version > 4 to run. + +Note that macOS is shipped with an older version of bash. Please ensure to upgrade before running the script. +
+

TL;DR

+

Obviously we strongly suggest reading through all the page to configure IntelOwl in the most appropriate way.

+

However, if you feel lazy, you could just install and test IntelOwl with the following steps. +docker will be run with sudo if permissions/roles have not been set.

+
# clone the IntelOwl project repository
+git clone https://github.com/intelowlproject/IntelOwl
+cd IntelOwl/
+
+# run helper script to verify installed dependencies and configure basic stuff
+./initialize.sh
+
+# start the app
+./start prod up
+# now the application is running on http://localhost:80
+
+# create a super user 
+sudo docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser
+
+# now you can login with the created user from http://localhost:80/login
+
+# Have fun!
+
+
+

Warning

+The first time you start IntelOwl, a lot of database migrations are being applied. This requires some time. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page. +
+

Deployment Components

+

IntelOwl is composed of various different technologies, namely: +* React: Frontend, using CRA and certego-ui +* Django: Backend +* PostgreSQL: Database +* Redis: Message Broker +* Celery: Task Queue +* Nginx: Reverse proxy for the Django API and web asssets. +* Uwsgi: Application Server +* Daphne: Asgi Server for WebSockets +* Elastic Search (optional): Auto-sync indexing of analysis' results. +* Kibana (optional): GUI for Elastic Search. We provide a saved configuration with dashboards and visualizations. +* Flower (optional): Celery Management Web Interface

+

All these components are managed via docker compose.

+

Deployment Preparation

+ +

Open a terminal and execute below commands to construct new environment files from provided templates.

+
./initialize.sh
+
+

Environment configuration (required)

+

In the docker/env_file_app, configure different variables as explained below.

+

REQUIRED variables to run the image: +* DB_HOST, DB_PORT, DB_USER, DB_PASSWORD: PostgreSQL configuration (The DB credentals should match the ones in the env_file_postgres). If you like, you can configure the connection to an external PostgreSQL instance in the same variables. Then, to avoid to run PostgreSQL locally, please run IntelOwl with the option --use-external-database. Otherwise, DB_HOST must be postgres to have the app properly communicate with the PostgreSQL container. +* DJANGO_SECRET: random 50 chars key, must be unique. If you do not provide one, Intel Owl will automatically set a secret key and use the same for each run. The key is generated by initialize.sh script.

+

Strongly recommended variable to set: +* INTELOWL_WEB_CLIENT_DOMAIN (example: localhost/mywebsite.com): the web domain of your instance, this is used for generating links to analysis results.

+

Optional configuration: +* OLD_JOBS_RETENTION_DAYS: Database retention for analysis results (default: 14 days). Change this if you want to keep your old analysis longer in the database.

+

Other optional configuration to enable specific services / features

+

Configuration required to enable integration with Slack: +* SLACK_TOKEN: Slack token of your Slack application that will be used to send/receive notifications +* DEFAULT_SLACK_CHANNEL: ID of the Slack channel you want to post the message to

+

Configuration required to enable Re-Captcha in the Login and the Registration Page: +In the docker/env_file_app: +* USE_RECAPTCHA: if you want to use recaptcha on your login +* RECAPTCHA_SECRET_KEY: your recaptcha secret key +In the frontend/public/env.js: +* RECAPTCHA_SITEKEY: Recaptcha Key for your site

+

Configuration required to have InteOwl sending Emails (registration requests, mail verification, password reset/change, etc) +* DEFAULT_FROM_EMAIL: email address used for automated correspondence from the site manager (example: noreply@mydomain.com) +* DEFAULT_EMAIL: email address used for correspondence with users (example: info@mydomain.com) +* EMAIL_HOST: the host to use for sending email with SMTP +* EMAIL_HOST_USER: username to use for the SMTP server defined in EMAIL_HOST +* EMAIL_HOST_PASSWORD: password to use for the SMTP server defined in EMAIL_HOST. This setting is used in conjunction with EMAIL_HOST_USER when authenticating to the SMTP server. +* EMAIL_PORT: port to use for the SMTP server defined in EMAIL_HOST. +* EMAIL_USE_TLS: whether to use an explicit TLS (secure) connection when talking to the SMTP server, generally used on port 587. +* EMAIL_USE_SSL: whether to use an implicit TLS (secure) connection when talking to the SMTP server, generally used on port 465.

+

Database configuration (required if running PostgreSQL locally)

+

If you use a local PostgreSQL instance (this is the default), in the env_file_postgres you have to configure different variables as explained below.

+

Required variables: +* POSTGRES_PASSWORD (same as DB_PASSWORD) +* POSTGRES_USER (same as DB_USER) +* POSTGRES_DB (default: intel_owl_db)

+ +

If you want to have your logs rotated correctly, we suggest you to add the configuration for the system Logrotate. +To do that you can leverage the initialize.sh script. Otherwise, if you have skipped that part, you can manually install logrotate by launching the following script: +

cd ./docker/scripts
+./install_logrotate.sh
+

+

We decided to do not leverage Django Rotation Configuration because it caused problematic concurrency issues, leading to logs that are not rotated correctly and to apps that do not log anymore. +Logrotate configuration is more stable.

+ +

We added few Crontab configurations that could be installed in the host machine at system level to solve some possible edge-case issues: +* Memory leaks: Once a week it is suggested to do a full restart of the application to clean-up the memory used by the application. Practical experience suggest us to do that to solve some recurrent memory issues in Celery. A cron called application_restart has been added for this purpose (it uses the absolute path of start script in the container). This cron assumes that you have executed IntelOwl with the parameters --all_analyzers. If you didn't, feel free to change the cron as you wish.

+

This configuration is optional but strongly recommended for people who want to have a production grade deployment. To install it you need to run the following script in each deployed server: +

cd ./docker/scripts
+./install_crontab.sh
+

+

Web server configuration (required for enabling HTTPS)

+

Intel Owl provides basic configuration for: +* Nginx (configuration/nginx/http.conf) +* Uwsgi (configuration/intel_owl.ini)

+

In case you enable HTTPS, remember to set the environment variable HTTPS_ENABLED as "enabled" to increment the security of the application.

+

There are 3 options to execute the web server:

+
    +
  • +

    HTTP only (default)

    +

    The project would use the default deployment configuration and HTTP only.

    +
  • +
  • +

    HTTPS with your own certificate

    +

    The project provides a template file to configure Nginx to serve HTTPS: configuration/nginx/https.conf.

    +

    You should change ssl_certificate, ssl_certificate_key and server_name in that file and put those required files in the specified locations.

    +

    Then you should call the ./start script with the parameter --https to leverage the right Docker Compose file for HTTPS.

    +

    Plus, if you use Flower, you should change in the docker/flower.override.yml the flower_http.conf with flower_https.conf.

    +
  • +
  • +

    HTTPS with Let's Encrypt

    +

    We provide a specific docker-compose file that leverages Traefik to allow fast deployments of public-faced and HTTPS-enabled applications.

    +

    Before using it, you should configure the configuration file docker/traefik.override.yml by changing the email address and the hostname where the application is served. For a detailed explanation follow the official documentation: Traefix doc.

    +

    After the configuration is done, you can add the option --traefik while executing ./start

    +
  • +
+

Run

+
+

Important Info

+IntelOwl depends heavily on docker and docker compose so as to hide this complexity from the enduser the project +leverages a custom shell script (start) to interface with docker compose. + +You may invoke $ ./start --help to get help and usage info. + +The CLI provides the primitives to correctly build, run or stop the containers for IntelOwl. Therefore, +
    +
  • It is possible to attach every optional docker container that IntelOwl has: +multi_queue with traefik enabled while every optional docker analyzer is active.
  • +
  • It is possible to insert an optional docker argument that the CLI will pass to docker-compose
  • +
+
+

Now that you have completed different configurations, starting the containers is as simple as invoking:

+
$ ./start prod up
+
+

You can add the docker options -d to run the application in the background.

+
+

Important Info

+All docker and docker compose specific options must be passed at the end of the script, after a -- token. +This token indicates the end of IntelOwl's options and the beginning of Docker options. + +Example: +
./start prod up -- -d
+
+
+
+

Hint

+Starting from IntelOwl 4.0.0, with the startup script you can select which version of IntelOwl you want to run (--version). +This can be helpful to keep using old versions in case of retrocompatibility issues. The --version parameter checks out the Git Repository to the Tag of the version that you have chosen. This means that if you checkout to a v3.x.x version, you won't have the --version parameter anymore so you would need to manually checkout back to the master branch to use newer versions. +
+

Stop

+

To stop the application you have to: +* if executed without -d parameter: press CTRL+C +* if executed with -d parameter: ./start prod down

+

Cleanup of database and application

+

This is a destructive operation but can be useful to start again the project from scratch

+

./start prod down -- -v

+

After Deployment

+

Users creation

+

You may want to run docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser after first run to create a superuser. +Then you can add other users directly from the Django Admin Interface after having logged with the superuser account. +To manage users, organizations and their visibility please refer to this section

+

Update and Rebuild

+

Rebuilding the project / Creating custom docker build

+

If you make some code changes and you like to rebuild the project, follow these steps:

+
    +
  1. ./start test build -- --tag=<your_tag> . to build the new docker image.
  2. +
  3. Add this new image tag in the docker/test.override.yml file.
  4. +
  5. Start the containers with ./start test up -- --build.
  6. +
+

Update to the most recent version

+

To update the project with the most recent available code you have to follow these steps:

+
$ cd <your_intel_owl_directory> # go into the project directory
+$ git pull # pull new changes
+$ ./start prod down # kill and destroy the currently running IntelOwl containers 
+$ ./start prod up # restart the IntelOwl application
+
+
+

Note

+After an upgrade, sometimes a database error in Celery Containers could happen. That could be related to new DB migrations which are not applied by the main Uwsgi Container yet. Do not worry. Wait few seconds for the Uwsgi container to start correctly, then put down the application again and restart it. The problem should be solved. If not, please feel free to open an issue on Github +
+
+

Note

+After having upgraded IntelOwl, in case the application does not start and you get an error like this: + +
PermissionError: [Errno 13] Permission denied: '/var/log/intel_owl/django/authentication.log
+
+ +just run this: +
sudo chown -R www-data:www-data /var/lib/docker/volumes/intel_owl_generic_logs/_data/django
+
+ +and restart IntelOwl. It should solve the permissions problem. +
+
+

Warning

+Major versions of IntelOwl are usually incompatible from one another. +Maintainers strive to keep the upgrade between major version easy but it's not always like that. +Below you can find the additional process required to upgrade from each major versions. +
+

Updating to >=6.0.0 from a 5.x.x version

+

IntelOwl v6 introduced some major changes regarding how the project is started. +Before upgrading, some important things should be checked by the administrator: +* Docker Compose V1 support has been dropped project-wide. If you are still using a Compose version prior to v2.3.4, please upgrade to a newer version or install Docker Compose V2. +* IntelOwl is now started with the new Bash start script that has the same options as the old Python start.py script but is more manageable and has decreased the overall project dependencies. The start.py script has now been removed. Please use the new start script instead. +* The default message broker is now Redis. We have replaced Rabbit-MQ for Redis to allow support for Websockets in the application: + * This change is transparent if you use our start script to run IntelOwl. That would spawn a Redis instance instead of a Rabbit-MQ one locally. + * If you were using an external broker like AWS SQS or a managed Rabbit-MQ, they are still supported but we suggest to move to a Redis supported service to simplify the architecture (because Redis is now mandatory for Websockets) +* Support for multiple jobs with multiple playbooks has been removed. Every Observable or File in the request will be processed by a single playbook. +* We upgraded the base PostgreSQL image from version 12 to version 16. You have 2 choice: + * remove your actual database and start from scratch with a new one + * maintain your database and do not update Postgres. This could break the application at anytime because we do not support it anymore. + * if you want to keep your old DB, follow the migration procedure you can find below

+
+

Warning

+CARE! We are providing this database migration procedure to help the users to migrate to a new PostgreSQL version. + +Upgrading PostgreSQL is outside the scope of the IntelOwl project so we do not guarantee that everything will work as intended. + +In case of doubt, please check the official PostgreSQL documentation. + +Upgrade at your own risk. +
+

The database migration procedure is as follows: +- You have IntelOwl version 5.x.x up and running +- Bring down the application (you can use the start script or manually concatenate your docker compose configuration ) +- Go inside the docker folder cd docker +- Bring only the postgres 12 container up docker run -d --name intelowl_postgres_12 -v intel_owl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:12-alpine +- Dump the entire database. You need the user and the database that you configured during startup for this docker exec -t intelowl_postgres_12 pg_dump -U <POSTGRES_USER> -d <POSTGRES_DB> --no-owner > /tmp/dump_intelowl.sql +- Stop che container docker container stop intelowl_postgres_12 +- Remove the backup container docker container rm intelowl_postgres_12 +- Remove the postgres volume docker volume rm intel_owl_postgres_data <------------- remove old data, this is not exactly necessary because the new postgres has a different volume name +- Start the intermediary postgres 16 container docker run -d --name intelowl_postgres_16 -v intelowl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:16-alpine +- Add the data to the volume cat /tmp/dump_intelowl.sql | docker exec -i intelowl_postgres_16 psql -U <POSTGRES_USER> -d <POSTGRES_DB> +- Stop the intermediary container docker container stop intelowl_postgres_16 +- Remove the intermediary container docker container rm intelowl_postgres_16 +- Update IntelOwl to the latest version +- Bring up the application back again (you can use the start script or manually concatenate your docker compose configuration)

+

Updating to >=5.0.0 from a 4.x.x version

+

IntelOwl v5 introduced some major changes regarding how the plugins and their related configuration are managed in the application. +Before upgrading, some important things should be checked by the administrator: +* A lot of database migrations will need to be applied. Just be patient few minutes once you install the new major release. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page. +* We moved away from the old big analyzer_config.json which was storing all the base configuration of the Analyzers to a database model (we did the same for all the other plugins types too). This allows us to manage plugins creation/modification/deletion in a more reliable manner and via the Django Admin Interface. If you have created custom plugins and changed those <plugins>_config.json file manually, you would need to re-create those custom plugins again from the Django Admin Interface. To do that please follow the related new documentation +* We have REMOVED all the analyzers that we deprecated during the v4 releases cycle. Please substitute them with their respective new names, in case they have a replacement. + * REMOVED Pulsedive_Active_IOC analyzer. Please substitute it with the new Pulsedive analyzer. + * REMOVED Fortiguard analyzer because endpoint does not work anymore. No substitute. + * REMOVED Rendertron analyzer not working as intended. No substitute. + * REMOVED ThreatMiner, SecurityTrails and Robtex various analyzers and substituted with new versions. + * REMOVED Doc_Info_Experimental. Its functionality (XLM Macro parsing) is moved to Doc_Info + * REMOVED Strings_Info_Classic. Please use Strings_Info + * REMOVED Strings_Info_ML. Please use Strings_Info and set the parameter rank_strings to True + * REMOVED all Yara_Scan_<repo> analyzers. They all went merged in the single Yara analyzer + * REMOVED Darksearch_Query analyzer because the service does not exist anymore. No substitute. + * REMOVED UnpacMe_EXE_Unpacker. Please use UnpacMe + * REMOVED BoxJS_Scan_JavaScript. Please use BoxJS + * REMOVED all Anomali_Threatstream_<option> analyzers. Now we have a single Anomali_Threatstream analyzer. Use the parameters to select the specific API you need.

+

Updating to >=5.0.0 from a 3.x.x version

+

This is not supported. Please perform a major upgrade once at a time.

+

Updating to >=4.0.0 from a 3.x.x version

+

IntelOwl v4 introduced some major changes regarding the permission management, allowing an easier way to manage users and visibility. But that did break the previous available DB. +So, to migrate to the new major version you would need to delete your DB. To do that, you would need to delete your volumes and start the application from scratch. +

python3 start.py prod down -v
+
+Please be aware that, while this can be an important effort to manage, the v4 IntelOwl provides an easier way to add, invite and manage users from the application itself. See the Organization section.

+

Updating to >=2.0.0 from a 1.x.x version

+

Users upgrading from previous versions need to manually move env_file_app, env_file_postgres and env_file_integrations files under the new docker directory.

+

Updating to >v1.3.x from any prior version

+

If you are updating to >v1.3.0 from any prior version, you need to execute a helper script so that the old data present in the database doesn't break.

+
    +
  1. +

    Follow the above updation steps, once the docker containers are up and running execute the following in a new terminal

    +
    docker exec -ti intelowl_uwsgi bash
    +
    +

    to get a shell session inside the IntelOwl's container.

    +
  2. +
  3. +

    Now just copy and paste the below command into this new session,

    +
    curl https://gist.githubusercontent.com/Eshaan7/b111f887fa8b860c762aa38d99ec5482/raw/758517acf87f9b45bd22f06aee57251b1f3b1bbf/update_to_v1.3.0.py | python -
    +
    +
  4. +
  5. +

    If you see "Update successful!", everything went fine and now you can enjoy the new features!

    +
  6. +
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Introduction/index.html b/Submodules/IntelOwl/docs/source/Introduction/index.html new file mode 100644 index 0000000..5520427 --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Introduction/index.html @@ -0,0 +1,635 @@ + + + + + + + + + + + +Introduction - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Introduction

+

IntelOwl was designed with the intent to help the community, in particular those researchers that can not afford commercial solutions, in the generation of threat intelligence data, in a simple, scalable and reliable way.

+

Main features:

+
    +
  • Provides enrichment of Threat Intel for malware as well as observables (IP, Domain, URL, hash, etc).
  • +
  • This application is built to scale out and to speed up the retrieval of threat info.
  • +
  • Thanks to the official libraries pyintelowl and go-intelowl, it can be integrated easily in your stack of security tools to automate common jobs usually performed, for instance, by SOC analysts manually.
  • +
  • Intel Owl is composed of:
  • +
  • analyzers that can be run to either retrieve data from external sources (like VirusTotal or AbuseIPDB) or to generate intel from internally available tools (like Yara or Oletools)
  • +
  • connectors that can be run to export data to external platforms (like MISP or OpenCTI)
  • +
  • visualizers that can be run to create custom visualizations of analyzers results
  • +
  • playbooks that are meant to make analysis easily repeatable
  • +
  • API REST written in Django and Python 3.9.
  • +
  • Built-in frontend client written in ReactJS, with certego-ui: provides features such as dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, etc.
  • +
+

Publications and media

+

To know more about the project and its growth over time, you may be interested in reading the following official blog posts and/or videos:

+ +

Feel free to ask everything it comes to your mind about the project to the author: +Matteo Lodi (Twitter).

+

We also have a dedicated twitter account for the project: @intel_owl.

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Redoc.rst b/Submodules/IntelOwl/docs/source/Redoc.rst new file mode 100644 index 0000000..c453d21 --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Redoc.rst @@ -0,0 +1,2 @@ +API docs +==================================== \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/Usage/index.html b/Submodules/IntelOwl/docs/source/Usage/index.html new file mode 100644 index 0000000..dad9f8b --- /dev/null +++ b/Submodules/IntelOwl/docs/source/Usage/index.html @@ -0,0 +1,1476 @@ + + + + + + + + + + + +Usage - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Usage

+

This page includes the most important things to know and understand when using IntelOwl.

+ +

How to interact with IntelOwl

+

Intel Owl main objective is to provide a single API interface to query in order to retrieve threat intelligence at scale.

+

There are multiple ways to interact with the Intel Owl APIs,

+
    +
  1. +

    Web Interface

    +
  2. +
  3. +

    Built-in Web interface with dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, tags management and more features

    +
  4. +
  5. +

    pyIntelOwl (CLI/SDK)

    +
  6. +
  7. +

    Official Python client that is available at: PyIntelOwl,

    +
  8. +
  9. Can be used as a library for your own python projects or...
  10. +
  11. +

    directly via the command line interface.

    +
  12. +
  13. +

    goIntelOwl (CLI/SDK)

    +
  14. +
  15. Official GO client that is available at: go-intelowl
  16. +
+
+

Hint: Tokens Creation

+The server authentication is managed by API tokens. So, if you want to interact with Intel Owl, you have two ways to do that: +
    +
  • If you are a normal user, you can go to the "API Access/Sessions" section of the GUI and create a Token there.
  • +
  • If you are an administrator of IntelOwl, you can create one or more unprivileged users from the Django Admin Interface and then generate a token for those users. +
  • +
+Afterwards you can leverage the created tokens with the Intel Owl Client. +
+

Plugins Framework

+

Plugins are the core modular components of IntelOwl that can be easily added, changed and customized. +There are several types of plugins:

+ +

Analyzers

+

Analyzers are the most important plugins in IntelOwl. They allow to perform data extraction on the observables and/or files that you would like to analyze.

+

Analyzers list

+

The following is the list of the available analyzers you can run out-of-the-box. You can also navigate the same list via the

+
    +
  • Graphical Interface: once your application is up and running, go to the "Plugins" section
  • +
  • pyintelowl: $ pyintelowl get-analyzer-config
  • +
+
File analyzers:
+
Internal tools
+ +
External services
+
    +
  • CapeSandbox: CAPESandbox automatically scans suspicious files using the CapeSandbox API. Analyzer works for private instances as well.
  • +
  • Cymru_Hash_Registry_Get_File: Check if a particular file is known to be malware by Team Cymru
  • +
  • Cuckoo_Scan: scan a file on Cuckoo (this analyzer is disabled by default. You have to change that flag in the config to use it)
  • +
  • DocGuard_Upload_File: Analyze office files in seconds. DocGuard.
  • +
  • Dragonfly_Emulation: Emulate malware against Dragonfly sandbox by Certego S.R.L.
  • +
  • FileScan_Upload_File: Upload your file to extract IoCs from executable files, documents and scripts via FileScan.io API.
  • +
  • HashLookupServer_Get_File: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
  • +
  • HybridAnalysis_Get_File: check file hash on HybridAnalysis sandbox reports
  • +
  • Intezer_Scan: scan a file on Intezer. Register for a free community account here. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • Malpedia_Scan: scan a binary or a zip file (pwd:infected) against all the yara rules available in Malpedia
  • +
  • MalwareBazaar_Get_File: Check if a particular malware sample is known to MalwareBazaar
  • +
  • MISPFIRST_Check_Hash: check a file hash on the FIRST MISP instance
  • +
  • MISP_Check_Hash: check a file hash on a MISP instance
  • +
  • MWDB_Scan: mwdblib Retrieve malware file analysis from repository maintained by CERT Polska MWDB. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • OTX_Check_Hash: check file hash on Alienvault OTX
  • +
  • SublimeSecurity: Analyze an Email with Sublime Security live flow
  • +
  • Triage_Scan: leverage Triage sandbox environment to scan various files
  • +
  • UnpacMe: UnpacMe is an automated malware unpacking service
  • +
  • Virushee_Scan: Check file hash on Virushee API. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • VirusTotal_v3_File: check the file hash on VirusTotal. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
  • +
  • YARAify_File_Scan: scan a file against public and non-public YARA and ClamAV signatures in YARAify public service
  • +
  • YARAify_File_Search: scan an hash against YARAify database
  • +
  • Zippy_scan : Zippy: Fast method to classify text as AI or human-generated; takes in lzma,zlib,brotli as input based engines; ensemble being default.
  • +
  • Blint: Blint is a Binary Linter that checks the security properties and capabilities of your executables. Supported binary formats: - Android (apk, aab) - ELF (GNU, musl) - PE (exe, dll) - Mach-O (x64, arm64)
  • +
+
Observable analyzers (ip, domain, url, hash)
+
Internal tools
+
    +
  • CheckDMARC: An SPF and DMARC DNS records validator for domains.
  • +
  • DNStwist: Scan a url/domain to find potentially malicious permutations via dns fuzzing. dnstwist repo
  • +
  • Thug_URL_Info: Perform hybrid dynamic/static analysis on a URL using Thug low-interaction honeyclient
  • +
+
External services
+
    +
  • AbuseIPDB: check if an ip was reported on AbuseIPDB
  • +
  • Abusix: get abuse contacts of an IP address from Abusix
  • +
  • BGP Ranking: BGP-Ranking provides a way to collect such malicious activities, aggregate the information per ASN and provide a ranking model to rank the ASN from the most malicious to the less malicious ASN.
  • +
  • Anomali_Threatstream_PassiveDNS: Return information from passive dns of Anomali. On Anomali Threatstream PassiveDNS Api.
  • +
  • Auth0: scan an IP against the Auth0 API
  • +
  • BinaryEdge: Details about an Host. List of recent events for the specified host, including details of exposed ports and services using IP query and return list of subdomains known from the target domains using domain query
  • +
  • BitcoinAbuse : Check a BTC address against bitcoinabuse.com, a public database of BTC addresses used by hackers and criminals.
  • +
  • Censys_Search: scan an IP address against Censys View API
  • +
  • CheckPhish: CheckPhish can detect phishing and fraudulent sites.
  • +
  • CIRCLPassiveDNS: scan an observable against the CIRCL Passive DNS DB
  • +
  • CIRCLPassiveSSL: scan an observable against the CIRCL Passive SSL DB
  • +
  • Classic_DNS: Retrieve current domain resolution with default DNS
  • +
  • CloudFlare_DNS: Retrieve current domain resolution with CloudFlare DoH (DNS over HTTPS)
  • +
  • CloudFlare_Malicious_Detector: Leverages CloudFlare DoH to check if a domain is related to malware
  • +
  • Crowdsec: check if an IP was reported on Crowdsec Smoke Dataset
  • +
  • Cymru_Hash_Registry_Get_Observable: Check if a particular hash is available in the malware hash registry of Team Cymru
  • +
  • DNSDB: scan an observable against the Passive DNS Farsight Database (support both v1 and v2 versions)
  • +
  • DNS0_EU: Retrieve current domain resolution with DNS0.eu DoH (DNS over HTTPS)
  • +
  • DNS0_EU_Malicious_Detector: Check if a domain or an url is marked as malicious in DNS0.eu database (Zero service)
  • +
  • DocGuard_Get: check if an hash was analyzed on DocGuard. DocGuard
  • +
  • Feodo_Tracker: Feodo Tracker offers various blocklists, helping network owners to protect their users from Dridex and Emotet/Heodo.
  • +
  • FileScan_Search: Finds reports and uploaded files by various tokens, like hash, filename, verdict, IOCs etc via FileScan.io API.
  • +
  • FireHol_IPList: check if an IP is in FireHol's IPList
  • +
  • GoogleSafebrowsing: Scan an observable against GoogleSafeBrowsing DB
  • +
  • GoogleWebRisk: Scan an observable against WebRisk API (Commercial version of Google Safe Browsing). Check the docs to enable this properly
  • +
  • Google_DNS: Retrieve current domain resolution with Google DoH (DNS over HTTPS)
  • +
  • GreedyBear: scan an IP or a domain against the GreedyBear API (requires API key)
  • +
  • GreyNoise: scan an IP against the Greynoise API (requires API key)
  • +
  • GreyNoiseCommunity: scan an IP against the Community Greynoise API (requires API key))
  • +
  • Greynoise_Labs: scan an IP against the Greynoise API (requires authentication token which can be obtained from cookies on Greynoise website after launching the playground from here)
  • +
  • HashLookupServer_Get_Observable: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
  • +
  • HoneyDB_Get: HoneyDB IP lookup service
  • +
  • HoneyDB_Scan_Twitter: scan an IP against HoneyDB.io's Twitter Threat Feed
  • +
  • Hunter_How: Scans IP and domain against Hunter_How API.
  • +
  • Hunter_Io: Scans a domain name and returns set of data about the organisation, the email address found and additional information about the people owning those email addresses.
  • +
  • HybridAnalysis_Get_Observable: search an observable in the HybridAnalysis sandbox reports
  • +
  • IP2WHOIS: API Docs IP2Location.io IP2WHOIS Domain WHOIS API helps users to obtain domain information and WHOIS record by using a domain name.
  • +
  • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
  • +
  • InQuest_DFI: Deep File Inspection by InQuest Labs
  • +
  • InQuest_IOCdb: Indicators of Compromise Database by InQuest Labs
  • +
  • InQuest_REPdb: Search in InQuest Lab's Reputation Database
  • +
  • IPApi: Get information about IPs using batch-endpoint and DNS using DNS-endpoint.
  • +
  • IPInfo: Location Information about an IP
  • +
  • Ip2location: API Docs IP2Location.io allows users to check IP address location in real time. (Supports both with or without key)
  • +
  • Intezer_Get: check if an analysis related to a hash is available in Intezer. Register for a free community account here.
  • +
  • Koodous: koodous API get information about android malware.
  • +
  • MalwareBazaar_Get_Observable: Check if a particular malware hash is known to MalwareBazaar
  • +
  • MalwareBazaar_Google_Observable: Check if a particular IP, domain or url is known to MalwareBazaar using google search
  • +
  • MaxMindGeoIP: extract GeoIP info for an observable
  • +
  • MISP: scan an observable on a MISP instance
  • +
  • MISPFIRST: scan an observable on the FIRST MISP instance
  • +
  • Mmdb_server: Mmdb_server mmdb-server is an open source fast API server to lookup IP addresses for their geographic location, AS number.
  • +
  • Mnemonic_PassiveDNS : Look up a domain or IP using the Mnemonic PassiveDNS public API.
  • +
  • MWDB_Get: mwdblib Retrieve malware file analysis by hash from repository maintained by CERT Polska MWDB.
  • +
  • Netlas: search an IP against Netlas
  • +
  • ONYPHE: search an observable in ONYPHE
  • +
  • OpenCTI: scan an observable on an OpenCTI instance
  • +
  • OTXQuery: scan an observable on Alienvault OTX
  • +
  • Phishstats: Search PhishStats API to determine if an IP/URL/domain is malicious.
  • +
  • Phishtank: Search an url against Phishtank API
  • +
  • PhishingArmy: Search an observable in the PhishingArmy blocklist
  • +
  • Pulsedive: Scan indicators and retrieve results from Pulsedive's API.
  • +
  • Quad9_DNS: Retrieve current domain resolution with Quad9 DoH (DNS over HTTPS)
  • +
  • Quad9_Malicious_Detector: Leverages Quad9 DoH to check if a domain is related to malware
  • +
  • Robtex: scan a domain/IP against the Robtex Passive DNS DB
  • +
  • Securitytrails: scan an IP/Domain against Securitytrails API
  • +
  • Shodan_Honeyscore: scan an IP against Shodan Honeyscore API
  • +
  • Shodan_Search: scan an IP against Shodan Search API
  • +
  • Spyse: Scan domains, IPs, emails and CVEs using Spyse's API. Register here.
  • +
  • SSAPINet: get a screenshot of a web page using screenshotapi.net (external source); additional config options can be added to extra_api_params in the config.
  • +
  • Stalkphish: Search Stalkphish API to retrieve information about a potential phishing site (IP/URL/domain/Generic).
  • +
  • Stratosphere_Blacklist: Cross-reference an IP from blacklists maintained by Stratosphere Labs
  • +
  • TalosReputation: check an IP reputation from Talos
  • +
  • ThreatFox: search for an IOC in ThreatFox's database
  • +
  • Threatminer: retrieve data from Threatminer API
  • +
  • TorNodesDanMeUk: check if an IP is a Tor Node using a list of all Tor nodes provided by dan.me.uk
  • +
  • TorProject: check if an IP is a Tor Exit Node
  • +
  • Triage_Search: Search for reports of observables or upload from URL on triage cloud
  • +
  • Tranco: Check if a domain is in the latest Tranco ranking top sites list
  • +
  • URLhaus: Query a domain or URL against URLhaus API.
  • +
  • UrlScan_Search: Search an IP/domain/url/hash against URLScan API
  • +
  • UrlScan_Submit_Result: Submit & retrieve result of an URL against URLScan API
  • +
  • Virushee_CheckHash: Search for a previous analysis of a file by its hash (SHA256/SHA1/MD5) on Virushee API.
  • +
  • VirusTotal_v3_Get_Observable: search an observable in the VirusTotal DB
  • +
  • Whoisxmlapi: Fetch WHOIS record data, of a domain name, an IP address, or an email address.
  • +
  • WhoIs_RipeDB_Search : Fetch whois record data of an IP address from Ripe DB using their search API (no API key required)
  • +
  • XForceExchange: scan an observable on IBM X-Force Exchange
  • +
  • YARAify_Search: lookup a file hash in Abuse.ch YARAify
  • +
  • YETI (Your Everyday Threat Intelligence): scan an observable on a YETI instance.
  • +
  • Zoomeye: Zoomeye Cyberspace Search Engine recording information of devices, websites, services and components etc..
  • +
  • Validin:Validin investigates historic and current data describing the structure and composition of the internet.
  • +
  • TweetFeed: TweetFeed collects Indicators of Compromise (IOCs) shared by the infosec community at Twitter.\r\nHere you will find malicious URLs, domains, IPs, and SHA256/MD5 hashes.
  • +
  • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
  • +
  • CyCat: CyCat or the CYbersecurity Resource CATalogue aims at mapping and documenting, in a single formalism and catalogue available cybersecurity tools, rules, playbooks, processes and controls.
  • +
  • Vulners: Vulners is the most complete and the only fully correlated security intelligence database, which goes through constant updates and links 200+ data sources in a unified machine-readable format. It contains 8 mln+ entries, including CVEs, advisories, exploits, and IoCs — everything you need to stay abreast on the latest security threats.
  • +
  • AILTypoSquatting:AILTypoSquatting is a Python library to generate list of potential typo squatting domains with domain name permutation engine to feed AIL and other systems.
  • +
+
Generic analyzers (email, phone number, etc.; anything really)
+

Some analyzers require details other than just IP, URL, Domain, etc. We classified them as generic Analyzers. Since the type of field is not known, there is a format for strings to be followed.

+
Internal tools
+
    +
  • CyberChef: Run a query on a CyberChef server using pre-defined or custom recipes.
  • +
+
External services
+
    +
  • Anomali_Threatstream_Confidence: Give max, average and minimum confidence of maliciousness for an observable. On Anomali Threatstream Confidence API.
  • +
  • Anomali_Threatstream_Intelligence: Search for threat intelligence information about an observable. On Anomali Threatstream Intelligence API.
  • +
  • CRXcavator: scans a chrome extension against crxcavator.io
  • +
  • Dehashed_Search: Query any observable/keyword against https://dehashed.com's search API.
  • +
  • EmailRep: search an email address on emailrep.io
  • +
  • HaveIBeenPwned: HaveIBeenPwned checks if an email address has been involved in a data breach
  • +
  • IntelX_Intelligent_Search: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
  • +
  • IntelX_Phonebook: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
  • +
  • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
  • +
  • MISP: scan an observable on a MISP instance
  • +
  • VirusTotal_v3_Intelligence_Search: Perform advanced queries with VirusTotal Intelligence (requires paid plan)
  • +
  • WiGLE: Maps and database of 802.11 wireless networks, with statistics, submitted by wardrivers, netstumblers, and net huggers.
  • +
  • YARAify_Generics: lookup a YARA rule (default), ClamAV rule, imphash, TLSH, telfhash or icon_dash in YARAify
  • +
  • PhoneInfoga : PhoneInfoga is one of the most advanced tools to scan international phone numbers.
  • +
  • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
  • +
+
Optional analyzers
+

Some analyzers are optional and need to be enabled explicitly.

+

Connectors

+

Connectors are designed to run after every successful analysis which makes them suitable for automated threat-sharing. They support integration with other SIEM/SOAR projects, specifically aimed at Threat Sharing Platforms.

+

Connectors list

+

The following is the list of the available connectors. You can also navigate the same list via the

+
    +
  • Graphical Interface: once your application is up and running, go to the "Plugins" section
  • +
  • pyintelowl: $ pyintelowl get-connector-config
  • +
+
List of pre-built Connectors
+
    +
  • MISP: automatically creates an event on your MISP instance, linking the successful analysis on IntelOwl.
  • +
  • OpenCTI: automatically creates an observable and a linked report on your OpenCTI instance, linking the the successful analysis on IntelOwl.
  • +
  • YETI: YETI = Your Everyday Threat Intelligence. find or create observable on YETI, linking the successful analysis on IntelOwl.
  • +
  • Slack: Send the analysis link to a Slack channel (useful for external notifications)
  • +
  • EmailSender: Send a generic email.
  • +
  • AbuseSubmitter: Send an email to request to take down a malicious domain.
  • +
+

Pivots

+

With IntelOwl v5.2.0 we introduced the Pivot Plugin.

+

Pivots are designed to create a job from another job. This plugin allows the user to set certain conditions that trigger the execution of one or more subsequent jobs, strictly connected to the first one.

+

This is a "SOAR" feature that allows the users to connect multiple analysis together.

+

List of pre-built Pivots

+
    +
  • TakedownRequestToAbuseIp: This Plugin leverages results from DNS resolver analyzers to extract a valid IP address to pivot to the Abusix analyzer.
  • +
  • AbuseIpToSubmission: This Plugin leverages results from the Abusix analyzer to extract the abuse contacts of an IP address to pivot to the AbuseSubmitter connector.
  • +
+

You can build your own custom Pivot with your custom logic with just few lines of code. See the Contribute section for more info.

+

Creating Pivots from the GUI

+

From the GUI, the users can pivot in two ways: +- If a Job executed a Visualizer, it is possible to select a field extracted and analyze its value by clicking the "Pivot" button (see following image). In this way, the user is able to "jump" from one indicator to another. +img.png

+
    +
  • Starting from an already existing Investigation, it is possible to select a Job block and click the "Pivot" button to analyze the same observable again, usually choosing another Playbook (see following image) +img.png
  • +
+

In both cases, the user is redirected to the Scan Page that is precompiled with the observable selected. Then the user would be able to select the Playbook to execute in the new job. +img.png

+

After the new Job is started, a new Investigation will be created (if it does not already exist) and both the jobs will be added to the same Investigation.

+

In the following image you can find an example of an Investigation composed by 3 pivots generated manually: +* leveraging the first way to create a Pivot, the 2 Jobs that analyzed IP addresses have been generated from the first test\.com Job +* leveraging the second way to create a Pivot, the second test\.com analysis had been created with a different Playbook.

+

img.png

+

Visualizers

+

With IntelOwl v5 we introduced a new plugin type called Visualizers. +You can leverage it as a framework to create custom aggregated and simplified visualization of analyzer results.

+

Visualizers are designed to run after the analyzers and the connectors. +The visualizer adds logic after the computations, allowing to show the final result in a different way than merely the list of reports.

+

Visualizers can be executed only during Scans through the playbook that has been configured on the visualizer itself.

+

This framework is extremely powerful and allows every user to customize the GUI as they wish. But you know...with great power comes great responsability. To fully leverage this framework, you would need to put some effort in place. You would need to understand which data is useful for you and then write few code lines that would create your own GUI. +To simplify the process, take example from the pre-built visualizers listed below and follow the dedicated documentation.

+
List of pre-built Visualizers
+
    +
  • DNS: displays the aggregation of every DNS analyzer report
  • +
  • Yara: displays the aggregation of every matched rule by the Yara Analyzer
  • +
  • Domain_Reputation: Visualizer for the Playbook "Popular_URL_Reputation_Services"
  • +
  • IP_Reputation: Visualizer for the Playbook "Popular_IP_Reputation_Services"
  • +
  • Pivot: Visualizer that can be used in a Playbook to show the Pivot execution result. See Pivots for more info.
  • +
+

Ingestors

+

With IntelOwl v5.1.0 we introduced the Ingestor Plugin.

+

Ingestors allow to automatically insert IOC streams from outside sources to IntelOwl itself. +Each Ingestor must have a Playbook attached: this will allow to create a Job from every IOC retrieved.

+

Ingestors are system-wide and disabled by default, meaning that only the administrator are able to configure them and enable them. +Ingestors can be spammy so be careful about enabling them.

+

A very powerful use is case is to combine Ingestors with Connectors to automatically extract data from external sources, analyze them with IntelOwl and push them externally to another platform (like MISP or a SIEM)

+

List of pre-built Ingestors

+
    +
  • ThreatFox: Retrieves daily ioc from https://threatfox.abuse.ch/ and analyze them.
  • +
+

Playbooks

+

Playbooks are designed to be easy to share sequence of running Plugins (Analyzers, Connectors, ...) on a particular kind of observable.

+

If you want to avoid to re-select/re-configure a particular combination of analyzers and connectors together every time, you should create a playbook out of it and use it instead. This is time saver.

+

This is a feature introduced since IntelOwl v4.1.0! Please provide feedback about it!

+

Playbooks List

+

The following is the list of the available pre-built playbooks. You can also navigate the same list via the

+
    +
  • Graphical Interface: once your application is up and running, go to the "Plugins" section
  • +
  • pyintelowl: $ pyintelowl get-playbook-config
  • +
+
List of pre-built playbooks
+
    +
  • FREE_TO_USE_ANALYZERS: A playbook containing all free to use analyzers.
  • +
  • Sample_Static_Analysis: A playbook containing all analyzers that perform static analysis on files.
  • +
  • Popular_URL_Reputation_Services: Collection of the most popular and free reputation analyzers for URLs and Domains
  • +
  • Popular_IP_Reputation_Services: Collection of the most popular and free reputation analyzers for IP addresses
  • +
  • Dns: A playbook containing all dns providers
  • +
  • Takedown_Request: Start investigation to request to take down a malicious domain. A mail will be sent to the domain's abuse contacts found
  • +
  • Abuse_IP: Playbook containing the Abusix analyzer. It is executed after the Takedown_Request playbook
  • +
  • Send_Abuse_Email: Playbook containing the AbuseSubmitter connector to send an email to request to take down a malicious domain. It is executed after the Abuse_IP playbook
  • +
+

Playbooks creation and customization

+

You can create new playbooks in different ways, based on the users you want to share them with:

+

If you want to share them to every user in IntelOwl, create them via the Django Admin interface at /admin/playbooks_manager/playbookconfig/.

+

If you want share them to yourself or your organization only, you need to leverage the "Save as Playbook" button that you can find on the top right of the Job Result Page. +In this way, after you have done an analysis, you can save the configuration of the Plugins you executed for re-use with a single click.

+

img.png

+

The created Playbook would be available to yourself only. If you want either to share it with your organization or to delete it, you need to go to the "Plugins" section and enable it manually by clicking the dedicated button.

+

img.png

+

Generic Plugin Creation, Configuration and Customization

+

If you want to create completely new Plugins (not based on already existing python modules), please refer to the Contribute section. This is usually the case when you want to integrate IntelOwl with either a new tool or a new service.

+

On the contrary, if you would like to just customize the already existing plugins, this is the place.

+

SuperUser customization

+

If you are an IntelOwl superuser, you can create, modify, delete analyzers based on already existing modules by changing the configuration values inside the Django Admin interface at: +- for analyzers: /admin/analyzers_manager/analyzerconfig/. +- for connectors: /admin/connectors_manager/connectorconfig/. +- ...and so on for all the Plugin types.

+

The following are the most important fields that you can change without touching the source code: +- Name: Name of the analyzer +- Description: Description of the analyzer +- Disabled: you can choose to disable certain analyzers, then they won't appear in the dropdown list and won't run if requested. +- Python Module: Python path of the class that will be executed. This should not be changed most of the times. +- Maximum TLP: see TLP Support +- Soft Time Limit: this is the maximum time (in seconds) of execution for an analyzer. Once reached, the task will be killed (or managed in the code by a custom Exception). Default 300. +- Routing Key: this takes effects only when multi-queue is enabled. Choose which celery worker would execute the task: local (ideal for tasks that leverage local applications like Yara), long (ideal for long tasks) or default (ideal for simple webAPI-based analyzers).

+

For analyzers only: +- Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a different mimetype from the ones you specified, it won't be executed +- Not Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a mimetype from the ones you specified, it won't be executed +- Observable Supported: can be populated as a list. If set, if you ask to analyze an observable that is not in this list, it won't be executed. Valid values are: ip, domain, url, hash, generic.

+

For connectors only: +- Run on Failure (default: true): if they can be run even if the job has status reported_with_fails

+

For visualizers only: +- Playbooks: list of playbooks that trigger the specified visualizer execution.

+

Sometimes, it may happen that you would like to create a new analyzer very similar to an already existing one. Maybe you would like to just change the description and the default parameters. +A helpful way to do that without having to copy/pasting the entire configuration, is to click on the analyzer that you want to copy, make the desired changes, and click the save as new button.

+
+

Warning

+Changing other keys can break a plugin. In that case, you should think about duplicating the configuration entry or python module with your changes. +
+

Other options can be added at the "Python module" level and not at the Plugin level. To do that, go to: admin/api_app/pythonmodule/ and select the Python module used by the Plugin that you want to change. +For example, the analyzer AbuseIPDB uses the Python module abuseipdb.AbuseIPDB.

+

img.png

+

Once there, you'll get this screen:

+

img.png

+

There you can change the following values: +- Update Schedule: if the analyzer require some sort of update (local database, local rules, ...), you can specify the crontab schedule to update them. +- Health Check Schedule: if the analyzer has implemented a Health Check, you can specify the crontab schedule to check whether the service works or not.

+

Parameters

+

Each Plugin could have one or more parameters available to be configured. These parameters allow the users to customize the Plugin behavior.

+

There are 2 types of Parameters: +* classic Parameters +* Secrets: these parameters usually manage sensitive data, like API keys.

+

To see the list of these parameters:

+
    +
  • You can view the "Plugin" Section in IntelOwl to have a complete and updated view of all the options available
  • +
  • You can view the parameters by exploring the Django Admin Interface:
  • +
  • admin/api_app/parameter/
  • +
  • or at the very end of each Plugin configuration like /admin/analyzers_manager/analyzerconfig/
  • +
+

You can change the Plugin Parameters at 5 different levels: +* if you are an IntelOwl superuser, you can go in the Django Admin Interface and change the default values of the parameters for every plugin you like. This option would change the default behavior for every user in the platform. +* if you are either Owner or Admin of an org, you can customize the default values of the parameters for every member of the organization by leveraging the GUI in the "Organization Config" section. This overrides the previous option. +* if you are a normal user, you can customize the default values of the parameters for your analysis only by leveraging the GUI in the "Plugin config" section. This overrides the previous option. +* You can choose to provide runtime configuration when requesting an analysis that will override the previous options. This override is done only for the specific analysis. See Customize analyzer execution at time of request

+
+

Playbook Exception

+Please remember that, if you are executing a Playbook, the "Runtime configuration" of the Playbook take precedence over the Plugin Configuration. +
+
+

Plugin Configuration Order

+Due to the multiple chances that are given to customize the parameters of the Plugins that are executed, it may be easy to confuse the order and launch Plugins without the awereness of what you are doing. + +This is the order to define which values are used for the parameters, starting by the most important element: +* Runtime Configuration at Time of Request. +* Runtime Configuration of the Playbook (if a Playbook is used and the Runtime Configuration at Time of Request is empty) +* Plugin Configuration of the User +* Plugin Configuration of the Organization +* Default Plugin Configuration of the Parameter + + +If you are using the GUI, please remember that you can always check the Parameters before starting a "Scan" by clicking at the "Runtime configuration" ![img.png](../static/runtime_config.png) button. + +Example: +![img.png](../static/runtime_config_2.png) +
+

Enabling or Disabling Plugins

+

By default, each available plugin is configured as either disabled or not. The majority of them are enabled by default, while others may be disabled to avoid potential problems in the application usability for first time users.

+

Considering the impact that this change could have in the application, the GUI does not allow a normal user to enable/disable any plugin. On the contrary, users with specific privileges may change this configuration: +* Org Administrators may leverage the feature documented here to enable/disable plugins for their org. This can be helpful to control users' behavior. +* IntelOwl Superusers (full admin) can go to the Django Admin Interface and enable/disable them from there. This operation does overwrite the Org administrators configuration. To find the plugin to change, they'll need to first choose the section of its type ("ANALYZERS_MANAGER", "CONNECTORS_MANAGER", etc), then select the chosen plugin, change the flag on that option and save the plugin by pressing the right button.

+

img.png

+

img.png

+

Special Plugins operations

+

All plugins, i.e. analyzers and connectors, have kill and retry actions. In addition to that, all docker-based analyzers and connectors have a healthcheck action to check if their associated instances are up or not.

+
    +
  • kill:
  • +
+

Stop a plugin whose status is running/pending:

+
    +
  • GUI: Buttons on reports table on job result page.
  • +
  • PyIntelOwl: IntelOwl.kill_analyzer and IntelOwl.kill_connector function.
  • +
  • CLI: $ pyintelowl jobs kill-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs kill-connector <job_id> <connector_name>
  • +
  • +

    API: PATCH /api/job/{job_id}/{plugin_type/{plugin_name}/kill and PATCH /api/job/{job_id}/connector/{connector_name}/kill

    +
  • +
  • +

    retry:

    +
  • +
+

Retry a plugin whose status is failed/killed:

+
    +
  • GUI: Buttons on reports table on job result page.
  • +
  • PyIntelOwl: IntelOwl.retry_analyzer and IntelOwl.retry_connector function,
  • +
  • CLI: $ pyintelowl jobs retry-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs retry-connector <job_id> <connector_name>
  • +
  • +

    API: PATCH /api/job/{job_id}/{plugin_type}/{plugin_name}/retry

    +
  • +
  • +

    healthcheck:

    +
  • +
+

Check if a plugin is able to connect to its provider:

+
    +
  • GUI: Buttons on every plugin table.
  • +
  • PyIntelOwl: IntelOwl.analyzer_healthcheck and IntelOwl.connector_healthcheck methods.
  • +
  • CLI: $ pyintelowl analyzer-healthcheck <analyzer_name> and $ pyintelowl connector-healthcheck <connector_name>
  • +
  • +

    API: GET /api/{plugin_type}/{plugin_name}/healthcheck

    +
  • +
  • +

    pull:

    +
  • +
+

Update a plugin with the newest rules/database:

+
    +
  • GUI: Buttons on every plugin table.
  • +
  • API: POST /api/{plugin_type}/{plugin_name}/pull
  • +
+

TLP Support

+

The Traffic Light Protocol (TLP) is a standard that was created to facilitate greater sharing of potentially sensitive information and more effective collaboration.

+

IntelOwl is not a threat intel sharing platform, like the MISP platform. However, IntelOwl is able to share analysis results to external platforms (via Connectors) and to send possible privacy related information to external services (via Analyzers).

+

This is why IntelOwl does support a customized version of the Traffic Light Protocol (TLP): to allow the user to have a better knowledge of how their data are being shared.

+

Every Analyzer and Connector can be configured with a maximum_tlp value. +Based on that value, IntelOwl understands if the specific plugin is allowed or not to run (e.g. if maximum_tlp is GREEN, it would run for analysis with TLPs WHITE and GREEN only)

+

These is how every available TLP value behaves once selected for an analysis execution: +1. CLEAR: no restriction (WHITE was replaced by CLEAR in TLP v2.0, but WHITE is supported for retrocompatibility) +2. GREEN: disable analyzers that could impact privacy +3. AMBER (default): disable analyzers that could impact privacy and limit view permissions to my group +4. RED: disable analyzers that could impact privacy, limit view permissions to my group and do not use any external service

+

Running a plugin

+

A plugin can be run when all of the following requirements have been satisfied: +1. All the required parameters of the plugin have been configured +2. The plugin is not disabled +3. The plugin is not disabled for the user's organization +4. If the plugin has a health check schedule, the last check has to be successful +5. The TLP selected to run the plugin cannot be higher than the maximum TLP configured for that plugin +6. The observable classification or the file mimetype has to be supported by the plugin

+

Investigations Framework

+

Investigations are a new framework introduced in IntelOwl v6 with the goal to allow the users to connect the analysis they do with each other.

+

In this way the analysts can use IntelOwl as the starting point of their "Investigations", register their findings, correlate the information found, and collaborate...all in a single place.

+

Things to know about the framework: +* an Investigation is a superset of IntelOwl Jobs. It can have attached one or more existing IntelOwl Jobs +* an Investigation contains a "description" section that can be changed and updated at anytime with new information from the analysts. +* modification to the Investigation (description, jobs, etc) can be done by every member of the Organization where the creator of the Investigation belongs. However only they creator can delete an Investigation.

+

Create and populate an investigation

+

Investigations are created in 2 ways: + * automatically: + * if you scan multiple observables at the same time, a new investigation will be created by default and all the observables they will be automatically connected to the same investigation. + * if you run a Job with a Playbook which contains a Pivot that triggers another Job, a new investigation will be created and both the Jobs will be added to the same investigation. See how you can create a new Pivot manually from the GUI. + * manually: by clicking on the button in the "History" section you can create an Investigation from scratch without any job attached (see following image)

+

img.png

+

If you want to add a job to an Investigation, you should click to the root block of the Investigation (see following image):

+

img_1.png

+

Once a job has been added, you'll have something like this:

+

img.png

+

If you want to remove a Job, you can click on the Job block and click "Remove branch". On the contrary, if you just want to see Job Results, you can click in the "Link" button. (check next image)

+

img.png

+

Example output of a complex investigation

+

investigation_screen.png

+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/docs/source/conf.py b/Submodules/IntelOwl/docs/source/conf.py new file mode 100644 index 0000000..ade44bc --- /dev/null +++ b/Submodules/IntelOwl/docs/source/conf.py @@ -0,0 +1,77 @@ +# This file is a part of IntelOwl https://github.com/intelowlproject/IntelOwl +# See the file 'LICENSE' for copying permission. + +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +from docutils.parsers.rst import directives +from sphinx.directives.code import CodeBlock + +directives.register_directive("code", CodeBlock) + + +# -- Project information ----------------------------------------------------- + +project = "IntelOwl" +copyright = "2022, Matteo Lodi" +author = "Matteo Lodi" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "recommonmark", + "sphinx_rtd_theme", + "sphinxcontrib.openapi", + "sphinxcontrib.redoc", +] +redoc_uri = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" + +source_suffix = [".rst", ".md"] + +master_doc = "index" + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +redoc = [ + { + "name": "API Docs", + "page": "Redoc", + "spec": "schema.yml", + "opts": {"suppress-warnings": True, "hide-hostname": True}, + } +] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] diff --git a/Submodules/IntelOwl/docs/source/index.rst b/Submodules/IntelOwl/docs/source/index.rst new file mode 100644 index 0000000..763103a --- /dev/null +++ b/Submodules/IntelOwl/docs/source/index.rst @@ -0,0 +1,18 @@ +.. IntelOwl documentation master file, created by + sphinx-quickstart on Mon Feb 24 12:14:15 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to IntelOwl's documentation! +==================================== + +.. toctree:: + :maxdepth: 2 + + Introduction + Installation + Advanced-Configuration + Usage + Advanced-Usage + Contribute + Redoc diff --git a/Submodules/IntelOwl/docs/source/schema.yml b/Submodules/IntelOwl/docs/source/schema.yml new file mode 100644 index 0000000..e21cbf6 --- /dev/null +++ b/Submodules/IntelOwl/docs/source/schema.yml @@ -0,0 +1,3088 @@ +openapi: 3.0.3 +info: + title: IntelOwl API specification + version: 6.0.4 +paths: + /api/analyze_file: + post: + operationId: analyze_file_create + description: + This endpoint allows to start a Job related for a single File. + Retained for retro-compatibility + tags: + - analyze_file + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/FileAnalysis" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/FileAnalysis" + multipart/form-data: + schema: + $ref: "#/components/schemas/FileAnalysis" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/AnalysisResponse" + description: "" + /api/analyze_multiple_files: + post: + operationId: analyze_multiple_files_create + description: This endpoint allows to start Jobs related to multiple Files + tags: + - analyze_multiple_files + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/MultipleFiles" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/MultipleFiles" + multipart/form-data: + schema: + $ref: "#/components/schemas/MultipleFiles" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/MultiAnalysisResponseList' + description: '' + /api/analyze_multiple_observables: + post: + operationId: analyze_multiple_observables_create + description: |- + This endpoint allows to start Jobs related to multiple observables. + Observable parameter must be composed like this: + [(, ), ...] + tags: + - analyze_multiple_observables + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/MultipleObservable" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/MultipleObservable" + multipart/form-data: + schema: + $ref: "#/components/schemas/MultipleObservable" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/MultiAnalysisResponseList' + description: '' + /api/analyze_observable: + post: + operationId: analyze_observable_create + description: + This endpoint allows to start a Job related to an observable. Retained + for retro-compatibility + tags: + - analyze_observable + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ObservableAnalysis" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/ObservableAnalysis" + multipart/form-data: + schema: + $ref: "#/components/schemas/ObservableAnalysis" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/AnalysisResponse" + description: "" + /api/analyzer/{name}/healthcheck: + get: + operationId: analyzer_healthcheck_retrieve + description: + "Health Check: if server instance associated with plugin is up + or not" + parameters: + - in: path + name: name + schema: + type: string + required: true + tags: + - analyzer + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/PluginHealthCheckSuccessResponse' + description: '' + /api/analyzer/{name}/update: + post: + operationId: analyzer_update_create + description: Update plugin with latest configuration + parameters: + - in: path + name: name + schema: + type: string + required: true + tags: + - analyzer + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PluginUpdateSuccessResponse' + description: '' + /api/ask_analysis_availability: + post: + operationId: ask_analysis_availability_create + description: |2- + + This is useful to avoid repeating the same analysis multiple times. + By default this API checks if there are existing analysis related to the md5 in + status "running" or "reported_without_fails" + Also, you need to specify the analyzers needed because, otherwise, it is + highly probable that you won't get all the results that you expect + tags: + - ask_analysis_availability + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/JobAvailability" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/JobAvailability" + multipart/form-data: + schema: + $ref: "#/components/schemas/JobAvailability" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/AskAnalysisAvailabilitySuccessResponse" + description: "" + /api/ask_multi_analysis_availability: + post: + operationId: ask_multi_analysis_availability_create + description: |2- + + This is useful to avoid repeating the same analysis multiple times. + By default this API checks if there are existing analysis related to the md5 in + status "running" or "reported_without_fails" + Also, you need to specify the analyzers needed because, otherwise, it is + highly probable that you won't get all the results that you expect. + NOTE: This API is similar to ask_analysis_availability, but it allows multiple + md5s to be checked at the same time. + tags: + - ask_multi_analysis_availability + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/MultiJobAvailabilityList" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/MultiJobAvailabilityList" + multipart/form-data: + schema: + $ref: "#/components/schemas/MultiJobAvailabilityList" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/AskAnalysisAvailabilitySuccessResponseList" + description: "" + /api/auth/apiaccess: + get: + operationId: auth_apiaccess_retrieve + description: |- + Durin's APIAccessTokenView. + + - ``GET`` -> get token-client pair info + - ``POST`` -> create and get token-client pair info + - ``DELETE`` -> delete existing API access token + + .. versionadded:: 1.0.0 + tags: + - auth + security: + - durinAuth: [] + responses: + '200': + description: No response body + post: + operationId: auth_apiaccess_create + description: |- + Durin's APIAccessTokenView. + + - ``GET`` -> get token-client pair info + - ``POST`` -> create and get token-client pair info + - ``DELETE`` -> delete existing API access token + + .. versionadded:: 1.0.0 + tags: + - auth + security: + - durinAuth: [] + responses: + '200': + description: No response body + delete: + operationId: auth_apiaccess_destroy + description: |- + Durin's APIAccessTokenView. + + - ``GET`` -> get token-client pair info + - ``POST`` -> create and get token-client pair info + - ``DELETE`` -> delete existing API access token + + .. versionadded:: 1.0.0 + tags: + - auth + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/auth/google: + get: + operationId: auth_google_retrieve + description: This endpoint redirects to Google OAuth login. + tags: + - auth + security: + - durinAuth: [] + - {} + responses: + '200': + description: No response body + /api/auth/google-callback: + get: + operationId: auth_google_callback_retrieve + description: |- + Durin's Login View. + + This view will return a JSON response when valid ``username``, ``password`` and + (if not overwritten) ``client`` fields are POSTed to the view using + form data or JSON. + + It uses the default serializer provided by + Django-Rest-Framework (``rest_framework.authtoken.serializers.AuthTokenSerializer``) + to validate the user credentials. + + It is possible to customize LoginView behaviour by overriding the following + helper methods: + tags: + - auth + responses: + '200': + description: No response body + post: + operationId: auth_google_callback_create + description: |- + Durin's Login View. + + This view will return a JSON response when valid ``username``, ``password`` and + (if not overwritten) ``client`` fields are POSTed to the view using + form data or JSON. + + It uses the default serializer provided by + Django-Rest-Framework (``rest_framework.authtoken.serializers.AuthTokenSerializer``) + to validate the user credentials. + + It is possible to customize LoginView behaviour by overriding the following + helper methods: + tags: + - auth + responses: + '200': + description: No response body + /api/auth/login: + post: + operationId: auth_login_create + description: |- + Durin's Login View. + + This view will return a JSON response when valid ``username``, ``password`` and + (if not overwritten) ``client`` fields are POSTed to the view using + form data or JSON. + + It uses the default serializer provided by + Django-Rest-Framework (``rest_framework.authtoken.serializers.AuthTokenSerializer``) + to validate the user credentials. + + It is possible to customize LoginView behaviour by overriding the following + helper methods: + tags: + - auth + responses: + "200": + description: No response body + /api/auth/logout: + post: + operationId: auth_logout_create + description: |- + Durin's Logout View. + + This view accepts only a post request with an empty body. + It responds to Durin Token Authentication. On a successful request, + + 1. The token used to authenticate is deleted from + the database and can no longer be used to authenticate. + + 2. :meth:`django.contrib.auth.signals.user_logged_out` is called. + + :returns: 204 (No content) + tags: + - auth + security: + - durinAuth: [] + responses: + "200": + description: No response body + /api/auth/sessions: + get: + operationId: auth_sessions_list + description: |- + Durin's TokenSessionsViewSet. + + - Returns list of active sessions of authed user. + - Only ``list()`` and ``delete()`` operations. + + .. versionadded:: 1.0.0 + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + tags: + - auth + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TokenSessions" + description: "" + /api/auth/sessions/{id}: + delete: + operationId: auth_sessions_destroy + description: |- + Durin's TokenSessionsViewSet. + + - Returns list of active sessions of authed user. + - Only ``list()`` and ``delete()`` operations. + + .. versionadded:: 1.0.0 + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this auth token. + required: true + tags: + - auth + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/connector/{name}/healthcheck: + get: + operationId: connector_healthcheck_retrieve + description: + "Health Check: if server instance associated with plugin is up + or not" + parameters: + - in: path + name: name + schema: + type: string + required: true + tags: + - connector + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/PluginHealthCheckSuccessResponse" + description: "" + /api/get_analyzer_configs: + get: + operationId: get_analyzer_configs_retrieve + description: |2- + + Get and parse the `analyzer_config.json` file, + can be useful if you want to choose the analyzers programmatically + tags: + - get_analyzer_configs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/AnalyzerConfig" + description: "" + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/GetAnalyzerConfigsFailedResponse" + description: "" + /api/get_connector_configs: + get: + operationId: get_connector_configs_retrieve + description: Get and parse the `connector_config.json` file + tags: + - get_connector_configs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/ConnectorConfig" + description: "" + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/GetConnectorConfigsFailedResponse" + description: "" + /api/get_playbook_configs: + get: + operationId: get_playbook_configs_retrieve + description: Get and parse the `playbook_config.json` file, + tags: + - get_playbook_configs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/PlaybookConfig' + description: '' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/GetPlaybookConfigsFailedResponse' + description: '' + /api/jobs: + get: + operationId: jobs_list + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + parameters: + - name: file_mimetype + required: false + in: query + description: file_mimetype + schema: + type: string + - name: file_name + required: false + in: query + description: file_name + schema: + type: string + - name: finished_analysis_time__gte + required: false + in: query + description: finished_analysis_time__gte + schema: + type: string + - name: finished_analysis_time__lte + required: false + in: query + description: finished_analysis_time__lte + schema: + type: string + - name: is_sample + required: false + in: query + description: is_sample + schema: + type: string + - name: md5 + required: false + in: query + description: md5 + schema: + type: string + - name: name + required: false + in: query + description: name + schema: + type: string + - name: observable_classification + required: false + in: query + description: observable_classification + schema: + type: string + enum: + - ip + - url + - domain + - hash + - generic + - '' + - name: observable_name + required: false + in: query + description: observable_name + schema: + type: string + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + - name: received_request_time__gte + required: false + in: query + description: received_request_time__gte + schema: + type: string + - name: received_request_time__lte + required: false + in: query + description: received_request_time__lte + schema: + type: string + - name: status + required: false + in: query + description: status + schema: + type: string + enum: + - pending + - running + - reported_without_fails + - reported_with_fails + - killed + - failed + - name: tags + required: false + in: query + description: tags + schema: + type: string + - name: tlp + required: false + in: query + description: tlp + schema: + type: string + enum: + - WHITE + - GREEN + - AMBER + - RED + - name: type + required: false + in: query + description: type + schema: + type: string + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/PaginatedJobListList" + description: "" + /api/jobs/{job_id}/analyzer/{name}/kill: + patch: + operationId: jobs_analyzer_kill_partial_update + description: Kill running plugin by closing celery task and marking as killed + parameters: + - in: path + name: job_id + schema: + type: string + pattern: ^\d+$ + required: true + - in: path + name: name + schema: + type: string + pattern: ^\w+$ + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/jobs/{job_id}/analyzer/{name}/retry: + patch: + operationId: jobs_analyzer_retry_partial_update + description: Retry a plugin run if it failed/was killed previously + parameters: + - in: path + name: job_id + schema: + type: string + pattern: ^\d+$ + required: true + - in: path + name: name + schema: + type: string + pattern: ^\w+$ + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/jobs/{job_id}/connector/{name}/kill: + patch: + operationId: jobs_connector_kill_partial_update + description: Kill running plugin by closing celery task and marking as killed + parameters: + - in: path + name: job_id + schema: + type: string + pattern: ^\d+$ + required: true + - in: path + name: name + schema: + type: string + pattern: ^\w+$ + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/jobs/{job_id}/connector/{name}/retry: + patch: + operationId: jobs_connector_retry_partial_update + description: Retry a plugin run if it failed/was killed previously + parameters: + - in: path + name: job_id + schema: + type: string + pattern: ^\d+$ + required: true + - in: path + name: name + schema: + type: string + pattern: ^\w+$ + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/jobs/{id}: + get: + operationId: jobs_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this job. + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + delete: + operationId: jobs_destroy + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this job. + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/jobs/{id}/download_sample: + get: + operationId: jobs_download_sample_retrieve + description: Download file/sample associated with a job + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this job. + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + type: string + format: binary + description: "" + "400": + description: No response body + /api/jobs/{id}/kill: + patch: + operationId: jobs_kill_partial_update + description: Kill running job by closing celery tasks and marking as killed + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this job. + required: true + tags: + - jobs + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/jobs/aggregate/file_mimetype: + get: + operationId: jobs_aggregate_file_mimetype_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + /api/jobs/aggregate/md5: + get: + operationId: jobs_aggregate_md5_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + /api/jobs/aggregate/observable_classification: + get: + operationId: jobs_aggregate_observable_classification_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + /api/jobs/aggregate/observable_name: + get: + operationId: jobs_aggregate_observable_name_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + /api/jobs/aggregate/status: + get: + operationId: jobs_aggregate_status_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + /api/jobs/aggregate/type: + get: + operationId: jobs_aggregate_type_retrieve + description: + "\n REST endpoint to fetch list of jobs or retrieve/delete a\ + \ job with job ID.\n Requires authentication.\n " + tags: + - jobs + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: "" + /api/me/access: + get: + operationId: me_access_retrieve + description: "\n Returns user's access information.\n " + tags: + - me + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/UserAccess" + description: "" + "500": + content: + application/json: + schema: + $ref: "#/components/schemas/APIException" + description: "" + /api/me/invitations: + get: + operationId: me_invitations_list + description: Only ``list()`` and ``destroy()`` actions. + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + tags: + - me + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/InvitationsList" + description: "" + /api/me/invitations/{id}: + delete: + operationId: me_invitations_destroy + description: Only ``list()`` and ``destroy()`` actions. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this invitation. + required: true + tags: + - me + security: + - durinAuth: [] + responses: + "204": + description: No response body + /api/me/invitations/{id}/accept: + post: + operationId: me_invitations_accept_create + description: Accept an invitation by ID. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this invitation. + required: true + tags: + - me + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/InvitationsList" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/InvitationsList" + multipart/form-data: + schema: + $ref: "#/components/schemas/InvitationsList" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/InvitationsList" + description: "" + /api/me/invitations/{id}/decline: + post: + operationId: me_invitations_decline_create + description: Decline an invitation by ID. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this invitation. + required: true + tags: + - me + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/InvitationsList" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/InvitationsList" + multipart/form-data: + schema: + $ref: "#/components/schemas/InvitationsList" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/InvitationsList" + description: "" + /api/me/organization: + get: + operationId: me_organization_list + description: Get organization. + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + tags: + - me + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Organization" + description: "" + post: + operationId: me_organization_create + description: Create new organization. + tags: + - me + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Organization" + multipart/form-data: + schema: + $ref: "#/components/schemas/Organization" + required: true + security: + - durinAuth: [] + responses: + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + description: "" + /api/me/organization/invite: + post: + operationId: me_organization_invite_create + description: |- + Invite user to organization (accessible only to the organization owner). + + ``POST ~/organization/invite``. + tags: + - me + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Organization" + multipart/form-data: + schema: + $ref: "#/components/schemas/Organization" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + description: "" + /api/me/organization/leave: + post: + operationId: me_organization_leave_create + description: |- + Leave organization (accessible only to members). + + ``POST ~/organization/leave``. + tags: + - me + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Organization" + multipart/form-data: + schema: + $ref: "#/components/schemas/Organization" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + description: "" + /api/me/organization/remove_member: + post: + operationId: me_organization_remove_member_create + description: |- + Remove user's membership from organization (accessible only to the organization owner). + + ``POST ~/organization/remove_member``. + tags: + - me + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Organization" + multipart/form-data: + schema: + $ref: "#/components/schemas/Organization" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + description: "" + /api/notification: + get: + operationId: notification_list + description: Only ``list()`` and ``retrieve()`` actions. + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + - name: page + required: false + in: query + description: A page number within the paginated result set. + schema: + type: integer + - name: page_size + required: false + in: query + description: Number of results to return per page. + schema: + type: integer + tags: + - notification + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/PaginatedNotificationList" + description: "" + /api/notification/{id}: + get: + operationId: notification_retrieve + description: Only ``list()`` and ``retrieve()`` actions. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this notification. + required: true + tags: + - notification + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Notification" + description: "" + /api/notification/{id}/mark-as-read: + post: + operationId: notification_mark_as_read_create + description: Only ``list()`` and ``retrieve()`` actions. + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this notification. + required: true + tags: + - notification + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Notification" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Notification" + multipart/form-data: + schema: + $ref: "#/components/schemas/Notification" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/Notification' + description: '' + /api/playbook/analyze_multiple_files: + post: + operationId: playbook_analyze_multiple_files_create + description: This endpoint allows to start a Job related to a file + tags: + - playbook + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlaybookFileAnalysis' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PlaybookFileAnalysis' + multipart/form-data: + schema: + $ref: '#/components/schemas/PlaybookFileAnalysis' + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/AnalysisResponse' + description: '' + /api/playbook/analyze_multiple_observables: + post: + operationId: playbook_analyze_multiple_observables_create + description: This endpoint allows to start a Job related to an observable + tags: + - playbook + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PlaybookObservableAnalysis' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PlaybookObservableAnalysis' + multipart/form-data: + schema: + $ref: '#/components/schemas/PlaybookObservableAnalysis' + required: true + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AnalysisResponse' + description: '' + /api/playbook/cache_playbook: + post: + operationId: playbook_cache_playbook_create + tags: + - playbook + security: + - durinAuth: [] + responses: + '200': + description: No response body + /api/plugin-config: + get: + operationId: plugin_config_list + description: "\n REST endpoint to fetch list of PluginConfig or retrieve/delete\ + \ a CustomConfig.\n Requires authentication. Allows access to only authorized\ + \ CustomConfigs.\n " + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + tags: + - plugin-config + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PluginConfig' + description: '' + post: + operationId: plugin_config_create + description: "\n REST endpoint to fetch list of PluginConfig or retrieve/delete\ + \ a CustomConfig.\n Requires authentication. Allows access to only authorized\ + \ CustomConfigs.\n " + tags: + - plugin-config + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PluginConfig' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PluginConfig' + multipart/form-data: + schema: + $ref: '#/components/schemas/PluginConfig' + required: true + security: + - durinAuth: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/PluginConfig' + description: '' + /api/plugin-config/{id}: + get: + operationId: plugin_config_retrieve + description: "\n REST endpoint to fetch list of PluginConfig or retrieve/delete\ + \ a CustomConfig.\n Requires authentication. Allows access to only authorized\ + \ CustomConfigs.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this plugin config. + required: true + tags: + - plugin-config + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PluginConfig' + description: '' + put: + operationId: plugin_config_update + description: "\n REST endpoint to fetch list of PluginConfig or retrieve/delete\ + \ a CustomConfig.\n Requires authentication. Allows access to only authorized\ + \ CustomConfigs.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this plugin config. + required: true + tags: + - plugin-config + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PluginConfig' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PluginConfig' + multipart/form-data: + schema: + $ref: '#/components/schemas/PluginConfig' + required: true + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PluginConfig' + description: '' + patch: + operationId: plugin_config_partial_update + description: "\n REST endpoint to fetch list of PluginConfig or retrieve/delete\ + \ a CustomConfig.\n Requires authentication. Allows access to only authorized\ + \ CustomConfigs.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this plugin config. + required: true + tags: + - plugin-config + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchedPluginConfig' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/PatchedPluginConfig' + multipart/form-data: + schema: + $ref: '#/components/schemas/PatchedPluginConfig' + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PluginConfig' + description: '' + delete: + operationId: plugin_config_destroy + description: "\n REST endpoint to fetch list of PluginConfig or retrieve/delete\ + \ a CustomConfig.\n Requires authentication. Allows access to only authorized\ + \ CustomConfigs.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this plugin config. + required: true + tags: + - plugin-config + security: + - durinAuth: [] + responses: + '204': + description: No response body + /api/plugin-disable/: + get: + operationId: plugin_disable_retrieve + description: |- + This endpoint allows organization owners + and members to view plugin state. + tags: + - plugin-disable + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PluginStateViewerResponse' + description: '' + /api/plugin-disable/{plugin_type}/{plugin_name}/: + post: + operationId: plugin_disable_create + description: This endpoint allows organization owners to toggle plugin state. + parameters: + - in: path + name: plugin_name + schema: + type: string + required: true + - in: path + name: plugin_type + schema: + type: integer + required: true + tags: + - plugin-disable + security: + - durinAuth: [] + responses: + '201': + description: No response body + delete: + operationId: plugin_disable_destroy + description: This endpoint allows organization owners to toggle plugin state. + parameters: + - in: path + name: plugin_name + schema: + type: string + required: true + - in: path + name: plugin_type + schema: + type: integer + required: true + tags: + - plugin-disable + security: + - durinAuth: [] + responses: + '201': + description: No response body + /api/tags: + get: + operationId: tags_list + description: "\n REST endpoint to perform CRUD operations on ``Tag`` model.\n\ + \ Requires authentication.\n " + parameters: + - name: ordering + required: false + in: query + description: Which field to use when ordering the results. + schema: + type: string + tags: + - tags + security: + - durinAuth: [] + responses: + '200': + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Tag' + description: '' + post: + operationId: tags_create + description: "\n REST endpoint to perform CRUD operations on ``Tag`` model.\n\ + \ Requires authentication.\n " + tags: + - tags + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Tag' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/Tag' + multipart/form-data: + schema: + $ref: '#/components/schemas/Tag' + required: true + security: + - durinAuth: [] + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/Tag' + description: '' + /api/tags/{id}: + get: + operationId: tags_retrieve + description: + "\n REST endpoint to perform CRUD operations on ``Tag`` model.\n\ + \ Requires authentication.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this tag. + required: true + tags: + - tags + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Tag" + description: "" + put: + operationId: tags_update + description: + "\n REST endpoint to perform CRUD operations on ``Tag`` model.\n\ + \ Requires authentication.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this tag. + required: true + tags: + - tags + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Tag" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/Tag" + multipart/form-data: + schema: + $ref: "#/components/schemas/Tag" + required: true + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Tag" + description: "" + patch: + operationId: tags_partial_update + description: + "\n REST endpoint to perform CRUD operations on ``Tag`` model.\n\ + \ Requires authentication.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this tag. + required: true + tags: + - tags + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/PatchedTag" + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/PatchedTag" + multipart/form-data: + schema: + $ref: "#/components/schemas/PatchedTag" + security: + - durinAuth: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Tag" + description: "" + delete: + operationId: tags_destroy + description: + "\n REST endpoint to perform CRUD operations on ``Tag`` model.\n\ + \ Requires authentication.\n " + parameters: + - in: path + name: id + schema: + type: integer + description: A unique integer value identifying this tag. + required: true + tags: + - tags + security: + - durinAuth: [] + responses: + "204": + description: No response body +components: + schemas: + APIException: + type: object + properties: + errors: + type: object + additionalProperties: {} + required: + - errors + AnalysisResponse: + type: object + properties: + job_id: + type: integer + status: + type: string + warnings: + type: array + items: {} + analyzers_running: + type: array + items: {} + connectors_running: + type: array + items: {} + playbooks_running: + type: array + items: {} + required: + - analyzers_running + - connectors_running + - job_id + - status + AnalyzerConfig: + type: object + description: Serializer for `analyzer_config.json`. + properties: + name: + type: string + python_module: + type: string + maxLength: 128 + disabled: + type: boolean + description: + type: string + config: + $ref: "#/components/schemas/_Config" + secrets: + type: object + additionalProperties: + $ref: "#/components/schemas/_Secret" + params: + type: object + additionalProperties: + $ref: "#/components/schemas/_Param" + verification: + type: object + properties: + configured: + type: boolean + error_message: + type: string + nullable: true + missing_secrets: + type: array + items: + type: string + required: + - configured + - error_message + - missing_secrets + readOnly: true + extends: + type: string + type: + $ref: "#/components/schemas/AnalyzerConfigTypeEnum" + external_service: + type: boolean + leaks_info: + type: boolean + default: false + docker_based: + type: boolean + default: false + run_hash: + type: boolean + default: false + run_hash_type: + $ref: "#/components/schemas/RunHashTypeEnum" + supported_filetypes: + type: array + items: {} + default: [] + not_supported_filetypes: + type: array + items: {} + default: [] + observable_supported: + type: array + items: + $ref: "#/components/schemas/ObservableSupportedEnum" + default: [] + required: + - config + - disabled + - external_service + - name + - params + - python_module + - secrets + - type + - verification + AnalyzerConfigTypeEnum: + enum: + - file + - observable + type: string + AnalyzerReport: + type: object + description: AnalyzerReport model's serializer. + properties: + name: + type: string + maxLength: 128 + status: + $ref: "#/components/schemas/Status0b3Enum" + report: + type: object + additionalProperties: {} + errors: + type: array + items: + type: string + maxLength: 512 + process_time: + type: number + format: double + readOnly: true + start_time: + type: string + format: date-time + end_time: + type: string + format: date-time + runtime_configuration: + type: object + additionalProperties: {} + nullable: true + type: + type: string + default: analyzer + parent_playbook: + type: string + maxLength: 128 + required: + - name + - process_time + - status + AskAnalysisAvailabilitySuccessResponse: + type: object + properties: + status: + type: string + readOnly: true + job_id: + type: string + readOnly: true + required: + - job_id + - status + AskAnalysisAvailabilitySuccessResponseList: + type: object + properties: + count: + type: integer + results: + type: array + items: + $ref: "#/components/schemas/AskAnalysisAvailabilitySuccessResponse" + required: + - count + - results + BlankEnum: + enum: + - '' + ConfigTypeEnum: + enum: + - '1' + - '2' + type: string + ConnectorConfig: + type: object + description: Serializer for `connector_config.json`. + properties: + name: + type: string + python_module: + type: string + maxLength: 128 + disabled: + type: boolean + description: + type: string + config: + $ref: "#/components/schemas/_Config" + secrets: + type: object + additionalProperties: + $ref: "#/components/schemas/_Secret" + params: + type: object + additionalProperties: + $ref: "#/components/schemas/_Param" + verification: + type: object + properties: + configured: + type: boolean + error_message: + type: string + nullable: true + missing_secrets: + type: array + items: + type: string + required: + - configured + - error_message + - missing_secrets + readOnly: true + extends: + type: string + maximum_tlp: + $ref: '#/components/schemas/MaximumTlpEnum' + run_on_failure: + type: boolean + default: false + required: + - config + - disabled + - maximum_tlp + - name + - params + - python_module + - secrets + - verification + ConnectorReport: + type: object + description: ConnectorReport model's serializer. + properties: + name: + type: string + maxLength: 128 + status: + $ref: "#/components/schemas/Status0b3Enum" + report: + type: object + additionalProperties: {} + errors: + type: array + items: + type: string + maxLength: 512 + process_time: + type: number + format: double + readOnly: true + start_time: + type: string + format: date-time + end_time: + type: string + format: date-time + runtime_configuration: + type: object + additionalProperties: {} + nullable: true + type: + type: string + default: connector + parent_playbook: + type: string + maxLength: 128 + required: + - name + - process_time + - status + FileAnalysis: + type: object + description: |- + ``Job`` model's serializer for File Analysis. + Used for ``create()``. + properties: + id: + type: integer + readOnly: true + user: + type: integer + nullable: true + tlp: + $ref: "#/components/schemas/TlpEnum" + file: + type: string + format: uri + file_name: + type: string + file_mimetype: + type: string + runtime_configuration: + type: object + additionalProperties: {} + writeOnly: true + default: {} + analyzers_requested: + type: array + items: {} + connectors_requested: + type: array + items: {} + playbooks_requested: + type: array + items: + type: string + maxLength: 128 + tags_labels: + type: array + items: {} + required: + - file + - id + GetAnalyzerConfigsFailedResponse: + type: object + properties: + error: + type: string + readOnly: true + required: + - error + GetConnectorConfigsFailedResponse: + type: object + properties: + error: + type: string + readOnly: true + required: + - error + GetPlaybookConfigsFailedResponse: + type: object + properties: + error: + type: string + readOnly: true + required: + - error + InvitationsList: + type: object + description: |- + A ModelSerializer that takes additional arguments for + "fields", "omit" and "expand" in order to + control which fields are displayed, and whether to replace simple + values with complex, nested serializations + properties: + id: + type: integer + readOnly: true + organization: + $ref: "#/components/schemas/Organization" + created_at: + type: string + format: date-time + readOnly: true + status: + $ref: "#/components/schemas/InvitationsListStatusEnum" + required: + - created_at + - id + - organization + InvitationsListStatusEnum: + enum: + - pending + - accepted + - declined + type: string + Job: + type: object + description: Used for ``retrieve()`` + properties: + id: + type: integer + readOnly: true + user: + $ref: "#/components/schemas/User" + tags: + type: array + items: + $ref: "#/components/schemas/Tag" + readOnly: true + analyzer_reports: + type: array + items: + $ref: "#/components/schemas/AnalyzerReport" + readOnly: true + connector_reports: + type: array + items: + $ref: "#/components/schemas/ConnectorReport" + readOnly: true + permissions: + type: object + additionalProperties: + type: boolean + readOnly: true + is_sample: + type: boolean + md5: + type: string + maxLength: 32 + observable_name: + type: string + maxLength: 512 + observable_classification: + oneOf: + - $ref: '#/components/schemas/ObservableClassificationEnum' + - $ref: '#/components/schemas/BlankEnum' + file_name: + type: string + maxLength: 512 + file_mimetype: + type: string + maxLength: 80 + status: + $ref: "#/components/schemas/StatusFefEnum" + analyzers_requested: + type: array + items: + type: string + maxLength: 128 + connectors_requested: + type: array + items: + type: string + maxLength: 128 + playbooks_requested: + type: array + items: + type: string + maxLength: 128 + analyzers_to_execute: + type: array + items: + type: string + maxLength: 128 + connectors_to_execute: + type: array + items: + type: string + maxLength: 128 + playbooks_to_execute: + type: array + items: + type: string + maxLength: 128 + received_request_time: + type: string + format: date-time + readOnly: true + finished_analysis_time: + type: string + format: date-time + nullable: true + process_time: + type: number + format: double + nullable: true + tlp: + $ref: "#/components/schemas/TlpEnum" + errors: + type: array + items: + type: string + maxLength: 900 + nullable: true + required: + - analyzer_reports + - connector_reports + - id + - md5 + - permissions + - received_request_time + - tags + - user + JobAvailability: + type: object + description: Serializer for ask_analysis_availability + properties: + id: + type: integer + readOnly: true + md5: + type: string + maxLength: 128 + analyzers: + type: array + items: {} + playbooks: + type: array + items: {} + running_only: + type: boolean + default: false + minutes_ago: + type: integer + is_sample: + type: boolean + observable_name: + type: string + maxLength: 512 + observable_classification: + oneOf: + - $ref: '#/components/schemas/ObservableClassificationEnum' + - $ref: '#/components/schemas/BlankEnum' + file_name: + type: string + maxLength: 512 + file_mimetype: + type: string + maxLength: 80 + status: + $ref: "#/components/schemas/StatusFefEnum" + analyzers_requested: + type: array + items: + type: string + maxLength: 128 + connectors_requested: + type: array + items: + type: string + maxLength: 128 + playbooks_requested: + type: array + items: + type: string + maxLength: 128 + analyzers_to_execute: + type: array + items: + type: string + maxLength: 128 + connectors_to_execute: + type: array + items: + type: string + maxLength: 128 + playbooks_to_execute: + type: array + items: + type: string + maxLength: 128 + received_request_time: + type: string + format: date-time + readOnly: true + finished_analysis_time: + type: string + format: date-time + nullable: true + process_time: + type: number + format: double + nullable: true + tlp: + $ref: "#/components/schemas/TlpEnum" + errors: + type: array + items: + type: string + maxLength: 900 + nullable: true + file: + type: string + format: uri + user: + type: integer + nullable: true + tags: + type: array + items: + type: integer + required: + - id + - md5 + - received_request_time + JobList: + type: object + description: Used for ``list()``. + properties: + id: + type: integer + readOnly: true + user: + $ref: "#/components/schemas/User" + tags: + type: array + items: + $ref: "#/components/schemas/Tag" + readOnly: true + is_sample: + type: boolean + md5: + type: string + maxLength: 32 + observable_name: + type: string + maxLength: 512 + observable_classification: + oneOf: + - $ref: '#/components/schemas/ObservableClassificationEnum' + - $ref: '#/components/schemas/BlankEnum' + file_name: + type: string + maxLength: 512 + file_mimetype: + type: string + maxLength: 80 + status: + $ref: "#/components/schemas/StatusFefEnum" + analyzers_requested: + type: array + items: + type: string + maxLength: 128 + connectors_requested: + type: array + items: + type: string + maxLength: 128 + playbooks_requested: + type: array + items: + type: string + maxLength: 128 + analyzers_to_execute: + type: array + items: + type: string + maxLength: 128 + connectors_to_execute: + type: array + items: + type: string + maxLength: 128 + playbooks_to_execute: + type: array + items: + type: string + maxLength: 128 + received_request_time: + type: string + format: date-time + readOnly: true + finished_analysis_time: + type: string + format: date-time + nullable: true + process_time: + type: number + format: double + nullable: true + tlp: + $ref: "#/components/schemas/TlpEnum" + errors: + type: array + items: + type: string + maxLength: 900 + nullable: true + required: + - id + - md5 + - received_request_time + - tags + - user + MaximumTlpEnum: + enum: + - CLEAR + - GREEN + - AMBER + - RED + type: string + MultiAnalysisResponseList: + type: object + properties: + count: + type: boolean + results: + type: array + items: + $ref: "#/components/schemas/AnalysisResponse" + required: + - count + - results + MultiJobAvailabilityList: + type: object + properties: + count: + type: boolean + results: + type: array + items: + $ref: "#/components/schemas/JobAvailability" + required: + - count + - results + MultipleFiles: + type: object + properties: + files: + type: array + items: + type: string + format: uri + file_names: + type: array + items: + type: string + file_mimetypes: + type: array + items: + type: string + required: + - file_mimetypes + - file_names + - files + MultipleObservable: + type: object + properties: + observables: + type: array + items: + type: array + items: {} + maxItems: 2 + minItems: 2 + required: + - observables + Notification: + type: object + properties: + id: + type: integer + readOnly: true + title: + type: string + maxLength: 128 + body: + type: string + created_at: + type: string + format: date-time + readOnly: true + read: + type: boolean + readOnly: true + required: + - body + - created_at + - id + - read + - title + ObservableAnalysis: + type: object + description: |- + ``Job`` model's serializer for Observable Analysis. + Used for ``create()``. + properties: + id: + type: integer + readOnly: true + user: + type: integer + nullable: true + tlp: + $ref: '#/components/schemas/TlpEnum' + observable_name: + type: string + observable_classification: + type: string + runtime_configuration: + type: object + additionalProperties: {} + writeOnly: true + default: {} + analyzers_requested: + type: array + items: {} + connectors_requested: + type: array + items: {} + tags_labels: + type: array + items: {} + required: + - id + - observable_name + ObservableClassificationEnum: + enum: + - ip + - url + - domain + - hash + - generic + type: string + ObservableSupportedEnum: + enum: + - ip + - url + - domain + - hash + - generic + type: string + Organization: + type: object + description: |- + A ModelSerializer that takes additional arguments for + "fields", "omit" and "expand" in order to + control which fields are displayed, and whether to replace simple + values with complex, nested serializations + properties: + members_count: + type: integer + readOnly: true + owner: + allOf: + - $ref: "#/components/schemas/_UserMember" + readOnly: true + name: + type: string + maxLength: 32 + required: + - members_count + - name + - owner + PaginatedJobListList: + type: object + properties: + count: + type: integer + example: 123 + total_pages: + type: integer + example: 5 + results: + type: array + items: + $ref: "#/components/schemas/JobList" + PaginatedNotificationList: + type: object + properties: + count: + type: integer + example: 123 + total_pages: + type: integer + example: 5 + results: + type: array + items: + $ref: '#/components/schemas/Notification' + PatchedPluginConfig: + type: object + properties: + id: + type: integer + readOnly: true + organization: + type: string + nullable: true + value: + type: object + additionalProperties: {} + type: + $ref: '#/components/schemas/Type816Enum' + config_type: + $ref: '#/components/schemas/ConfigTypeEnum' + attribute: + type: string + maxLength: 128 + plugin_name: + type: string + maxLength: 128 + updated_at: + type: string + format: date-time + readOnly: true + PatchedTag: + type: object + properties: + id: + type: integer + readOnly: true + label: + type: string + maxLength: 50 + color: + type: string + maxLength: 7 + PlaybookConfig: + type: object + description: Serializer for `playbook_config.json`. + properties: + name: + type: string + python_module: + type: string + default: '' + disabled: + type: boolean + description: + type: string + config: + type: object + additionalProperties: {} + default: {} + secrets: + type: object + additionalProperties: {} + default: {} + params: + type: object + additionalProperties: {} + default: {} + verification: + type: object + additionalProperties: {} + default: {} + extends: + type: string + analyzers: + type: object + additionalProperties: + type: object + additionalProperties: {} + connectors: + type: object + additionalProperties: + type: object + additionalProperties: {} + supports: + type: array + items: + $ref: '#/components/schemas/SupportsEnum' + default: [] + required: + - analyzers + - connectors + - description + - disabled + - name + PlaybookFileAnalysis: + type: object + description: |- + ``Job`` model's serializer for File Analysis. + Used for ``create()``. + properties: + id: + type: integer + readOnly: true + user: + type: integer + nullable: true + tlp: + $ref: '#/components/schemas/TlpEnum' + file: + type: string + format: uri + file_name: + type: string + file_mimetype: + type: string + runtime_configuration: + type: object + additionalProperties: {} + writeOnly: true + default: {} + analyzers_requested: + type: array + items: {} + connectors_requested: + type: array + items: {} + playbooks_requested: + type: array + items: + type: string + maxLength: 128 + tags_labels: + type: array + items: {} + required: + - file + - id + PlaybookObservableAnalysis: + type: object + description: |- + ``Job`` model's serializer for Observable Analysis. + Used for ``create()``. + properties: + id: + type: integer + readOnly: true + user: + type: integer + nullable: true + tlp: + $ref: '#/components/schemas/TlpEnum' + observable_name: + type: string + observable_classification: + type: string + runtime_configuration: + type: object + additionalProperties: {} + writeOnly: true + default: {} + analyzers_requested: + type: array + items: {} + connectors_requested: + type: array + items: {} + playbooks_requested: + type: array + items: + type: string + maxLength: 128 + tags_labels: + type: array + items: {} + required: + - id + - observable_name + PluginConfig: + type: object + properties: + id: + type: integer + readOnly: true + organization: + type: string + nullable: true + value: + type: object + additionalProperties: {} + type: + $ref: '#/components/schemas/Type816Enum' + config_type: + $ref: '#/components/schemas/ConfigTypeEnum' + attribute: + type: string + maxLength: 128 + plugin_name: + type: string + maxLength: 128 + updated_at: + type: string + format: date-time + readOnly: true + required: + - attribute + - config_type + - id + - plugin_name + - type + - updated_at + - value + PluginHealthCheckSuccessResponse: + type: object + properties: + status: + type: boolean + nullable: true + required: + - status + PluginStateViewerResponse: + type: object + properties: + data: + type: object + additionalProperties: {} + required: + - data + PluginUpdateSuccessResponse: + type: object + properties: + status: + type: boolean + detail: + type: string + nullable: true + required: + - detail + - status + RunHashTypeEnum: + enum: + - md5 + - sha256 + type: string + Status0b3Enum: + enum: + - FAILED + - PENDING + - RUNNING + - SUCCESS + - KILLED + type: string + StatusFefEnum: + enum: + - pending + - running + - reported_without_fails + - reported_with_fails + - killed + - failed + type: string + SupportsEnum: + enum: + - ip + - url + - domain + - hash + - generic + - file + type: string + Tag: + type: object + properties: + id: + type: integer + readOnly: true + label: + type: string + maxLength: 50 + color: + type: string + maxLength: 7 + required: + - color + - id + - label + TlpEnum: + enum: + - CLEAR + - GREEN + - AMBER + - RED + type: string + TokenSessions: + type: object + description: |- + Used in :class:`durin.views.TokenSessionsViewSet`. + + .. versionadded:: 1.0.0 + properties: + id: + type: integer + readOnly: true + client: + type: string + description: A unique identification name for the client. + readOnly: true + created: + type: string + format: date-time + readOnly: true + expiry: + type: string + format: date-time + readOnly: true + has_expired: + type: boolean + readOnly: true + is_current: + type: boolean + readOnly: true + expires_in_str: + type: string + required: + - client + - created + - expires_in_str + - expiry + - has_expired + - id + - is_current + Type1d0Enum: + enum: + - int + - float + - str + - bool + - list + - dict + type: string + Type816Enum: + enum: + - '1' + - '2' + type: string + User: + type: object + properties: + username: + type: string + description: + Required. 150 characters or fewer. Letters, digits and @/./+/-/_ + only. + pattern: ^[\w.@+-]+$ + maxLength: 150 + required: + - username + UserAccess: + type: object + description: |- + Used by :class:`~.views.UserAccessView`. + + Ideally, this serializer should be extended inside your + service's source code adding user relevant information + and then point to it with the + ``USER_ACCESS_SERIALIZER`` settings variable. + properties: + user: + type: object + additionalProperties: {} + readOnly: true + access: + type: object + additionalProperties: {} + readOnly: true + required: + - access + - user + _Config: + type: object + description: |- + To validate `config` attr. + Used for `analyzer_config.json` and `connector_config.json` files. + properties: + queue: + type: string + soft_time_limit: + type: integer + required: + - queue + - soft_time_limit + _Param: + type: object + description: |- + To validate `params` attr. + Used for `analyzer_config.json` and `connector_config.json` files. + properties: + value: + type: string + type: + $ref: '#/components/schemas/Type1d0Enum' + description: + type: string + maxLength: 512 + required: + - description + - type + - value + _Secret: + type: object + description: |- + To validate `secrets` attr. + Used for `analyzer_config.json` and `connector_config.json` files. + properties: + env_var_key: + type: string + maxLength: 128 + description: + type: string + maxLength: 512 + required: + type: boolean + type: + $ref: '#/components/schemas/Type1d0Enum' + default: + type: string + required: + - description + - env_var_key + - required + _UserMember: + type: object + description: |- + A ModelSerializer that takes additional arguments for + "fields", "omit" and "expand" in order to + control which fields are displayed, and whether to replace simple + values with complex, nested serializations + properties: + username: + type: string + full_name: + type: string + joined: + type: string + format: date-time + required: + - full_name + - joined + - username + securitySchemes: + durinAuth: + type: apiKey + in: header + name: Authorization + description: "Token-based authentication with required prefix: Token" diff --git a/Submodules/IntelOwl/docs/static/Certego.png b/Submodules/IntelOwl/docs/static/Certego.png new file mode 100644 index 0000000..82d095b Binary files /dev/null and b/Submodules/IntelOwl/docs/static/Certego.png differ diff --git a/Submodules/IntelOwl/docs/static/abuseipdb.png b/Submodules/IntelOwl/docs/static/abuseipdb.png new file mode 100644 index 0000000..a6e002c Binary files /dev/null and b/Submodules/IntelOwl/docs/static/abuseipdb.png differ diff --git a/Submodules/IntelOwl/docs/static/accept_invite.png b/Submodules/IntelOwl/docs/static/accept_invite.png new file mode 100644 index 0000000..f82b2c6 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/accept_invite.png differ diff --git a/Submodules/IntelOwl/docs/static/add_job_to_investigation.png b/Submodules/IntelOwl/docs/static/add_job_to_investigation.png new file mode 100644 index 0000000..12cf2a7 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/add_job_to_investigation.png differ diff --git a/Submodules/IntelOwl/docs/static/create_investigation.png b/Submodules/IntelOwl/docs/static/create_investigation.png new file mode 100644 index 0000000..707f36b Binary files /dev/null and b/Submodules/IntelOwl/docs/static/create_investigation.png differ diff --git a/Submodules/IntelOwl/docs/static/disable_org.png b/Submodules/IntelOwl/docs/static/disable_org.png new file mode 100644 index 0000000..dfd24bb Binary files /dev/null and b/Submodules/IntelOwl/docs/static/disable_org.png differ diff --git a/Submodules/IntelOwl/docs/static/disabled.png b/Submodules/IntelOwl/docs/static/disabled.png new file mode 100644 index 0000000..b8c2aa2 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/disabled.png differ diff --git a/Submodules/IntelOwl/docs/static/gsoc_logo.png b/Submodules/IntelOwl/docs/static/gsoc_logo.png new file mode 100644 index 0000000..9c5ca29 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/gsoc_logo.png differ diff --git a/Submodules/IntelOwl/docs/static/honeynet_logo.png b/Submodules/IntelOwl/docs/static/honeynet_logo.png new file mode 100644 index 0000000..b6e5f96 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/honeynet_logo.png differ diff --git a/Submodules/IntelOwl/docs/static/intel_owl_negative.png b/Submodules/IntelOwl/docs/static/intel_owl_negative.png new file mode 100644 index 0000000..4ae036c Binary files /dev/null and b/Submodules/IntelOwl/docs/static/intel_owl_negative.png differ diff --git a/Submodules/IntelOwl/docs/static/intel_owl_positive.png b/Submodules/IntelOwl/docs/static/intel_owl_positive.png new file mode 100644 index 0000000..58fba9d Binary files /dev/null and b/Submodules/IntelOwl/docs/static/intel_owl_positive.png differ diff --git a/Submodules/IntelOwl/docs/static/investigation_screen.png b/Submodules/IntelOwl/docs/static/investigation_screen.png new file mode 100644 index 0000000..89d0f21 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/investigation_screen.png differ diff --git a/Submodules/IntelOwl/docs/static/job_options.png b/Submodules/IntelOwl/docs/static/job_options.png new file mode 100644 index 0000000..c9d9ea3 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/job_options.png differ diff --git a/Submodules/IntelOwl/docs/static/notifications.png b/Submodules/IntelOwl/docs/static/notifications.png new file mode 100644 index 0000000..c926b83 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/notifications.png differ diff --git a/Submodules/IntelOwl/docs/static/old_intel_owl.jpeg b/Submodules/IntelOwl/docs/static/old_intel_owl.jpeg new file mode 100644 index 0000000..1e63c1d Binary files /dev/null and b/Submodules/IntelOwl/docs/static/old_intel_owl.jpeg differ diff --git a/Submodules/IntelOwl/docs/static/old_intel_owl.png b/Submodules/IntelOwl/docs/static/old_intel_owl.png new file mode 100644 index 0000000..fb6dcbd Binary files /dev/null and b/Submodules/IntelOwl/docs/static/old_intel_owl.png differ diff --git a/Submodules/IntelOwl/docs/static/pivot_investigation.png b/Submodules/IntelOwl/docs/static/pivot_investigation.png new file mode 100644 index 0000000..9ab41a3 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/pivot_investigation.png differ diff --git a/Submodules/IntelOwl/docs/static/pivot_investigation_report.png b/Submodules/IntelOwl/docs/static/pivot_investigation_report.png new file mode 100644 index 0000000..9753a5b Binary files /dev/null and b/Submodules/IntelOwl/docs/static/pivot_investigation_report.png differ diff --git a/Submodules/IntelOwl/docs/static/pivot_job_report.png b/Submodules/IntelOwl/docs/static/pivot_job_report.png new file mode 100644 index 0000000..e7409a1 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/pivot_job_report.png differ diff --git a/Submodules/IntelOwl/docs/static/pivot_scan_page.png b/Submodules/IntelOwl/docs/static/pivot_scan_page.png new file mode 100644 index 0000000..dd516d9 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/pivot_scan_page.png differ diff --git a/Submodules/IntelOwl/docs/static/playbook_creation.png b/Submodules/IntelOwl/docs/static/playbook_creation.png new file mode 100644 index 0000000..3d418de Binary files /dev/null and b/Submodules/IntelOwl/docs/static/playbook_creation.png differ diff --git a/Submodules/IntelOwl/docs/static/playbooks_cr.png b/Submodules/IntelOwl/docs/static/playbooks_cr.png new file mode 100644 index 0000000..75b1c06 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/playbooks_cr.png differ diff --git a/Submodules/IntelOwl/docs/static/python_module_abuseipdb.png b/Submodules/IntelOwl/docs/static/python_module_abuseipdb.png new file mode 100644 index 0000000..d7e44e0 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/python_module_abuseipdb.png differ diff --git a/Submodules/IntelOwl/docs/static/runtime_config.png b/Submodules/IntelOwl/docs/static/runtime_config.png new file mode 100644 index 0000000..1c43517 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/runtime_config.png differ diff --git a/Submodules/IntelOwl/docs/static/runtime_config_2.png b/Submodules/IntelOwl/docs/static/runtime_config_2.png new file mode 100644 index 0000000..63b605a Binary files /dev/null and b/Submodules/IntelOwl/docs/static/runtime_config_2.png differ diff --git a/Submodules/IntelOwl/docs/static/save.png b/Submodules/IntelOwl/docs/static/save.png new file mode 100644 index 0000000..2ded433 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/save.png differ diff --git a/Submodules/IntelOwl/docs/static/simple_investigation.png b/Submodules/IntelOwl/docs/static/simple_investigation.png new file mode 100644 index 0000000..368930c Binary files /dev/null and b/Submodules/IntelOwl/docs/static/simple_investigation.png differ diff --git a/Submodules/IntelOwl/docs/static/threathunter_logo.png b/Submodules/IntelOwl/docs/static/threathunter_logo.png new file mode 100644 index 0000000..32298e4 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/threathunter_logo.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizableBool_example.png b/Submodules/IntelOwl/docs/static/visualizableBool_example.png new file mode 100644 index 0000000..15b67cf Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizableBool_example.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizableHlist_example.png b/Submodules/IntelOwl/docs/static/visualizableHlist_example.png new file mode 100644 index 0000000..4ea6206 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizableHlist_example.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizableLevel_example.png b/Submodules/IntelOwl/docs/static/visualizableLevel_example.png new file mode 100644 index 0000000..919479e Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizableLevel_example.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizablePage_example.png b/Submodules/IntelOwl/docs/static/visualizablePage_example.png new file mode 100644 index 0000000..2c59e88 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizablePage_example.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizableTable_example.png b/Submodules/IntelOwl/docs/static/visualizableTable_example.png new file mode 100644 index 0000000..a139d69 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizableTable_example.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizableTitle_example.png b/Submodules/IntelOwl/docs/static/visualizableTitle_example.png new file mode 100644 index 0000000..6e51132 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizableTitle_example.png differ diff --git a/Submodules/IntelOwl/docs/static/visualizableVlist_example.png b/Submodules/IntelOwl/docs/static/visualizableVlist_example.png new file mode 100644 index 0000000..fa77e65 Binary files /dev/null and b/Submodules/IntelOwl/docs/static/visualizableVlist_example.png differ diff --git a/Submodules/IntelOwl/frontend/babel.config.js b/Submodules/IntelOwl/frontend/babel.config.js new file mode 100644 index 0000000..48a4d79 --- /dev/null +++ b/Submodules/IntelOwl/frontend/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + "@babel/preset-env", + ["@babel/preset-react", { runtime: "automatic" }], + ], +}; diff --git a/Submodules/IntelOwl/frontend/index.html b/Submodules/IntelOwl/frontend/index.html new file mode 100644 index 0000000..ac86254 --- /dev/null +++ b/Submodules/IntelOwl/frontend/index.html @@ -0,0 +1,726 @@ + + + + + + + + + + + +IntelOwl - frontend - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

IntelOwl - frontend

+

Built with @certego/certego-ui.

+

Design thesis

+
    +
  • Re-usable components/hooks/stores that other projects can also benefit from should be added to certego-ui package.
  • +
  • IntelOwl specific:
  • +
  • components should be added to src/components/common.
  • +
  • general hooks should be added to src/hooks.
  • +
  • zustand stores hooks should be added to src/stores.
  • +
+

Directory Structure

+
public/                                   public static assets
+|- icons/                                 icons/favicon
+|- index.html/                            root HTML file
+src/                                      source code
+|- components/                            pages and components
+|  |- auth/                               `authentication` (login, logout, OAuth pages)
+|  |- common/                             small re-usable components
+|  |- dashboard/                          dashboard page and charts
+|  |- home/                               landing/home page
+|  |- jobs/                               `api_app`
+|  |  |- result/                          JobResult.jsx
+|  |  |- table/                           JobsTable.jsx
+|  |- me/
+|  |  |- organization/                    `certego_saas.apps.organization`
+|  |  |- sessions/                        durin (sessions management)
+|  |- misc/
+|  |  |- notification/                    `certego_saas.apps.notifications`
+|  |- plugins/                            `api_app.analyzers_manager`, `api_app.connectors_manager`
+|  |- scan/                               new scan/job
+|  |- Routes.jsx                          lazy route-component mappings
+|- constants/                             constant values
+|  |- api.js                              API URLs
+|  |- environment.js                      environment variables
+|  |- index.js                            intelowl specific constants
+|- hooks/                                 react hooks
+|- layouts/                               header, main, footer containers
+|- stores/                                zustand stores hooks
+|- styles/                                scss files
+|- utils/                                 utility functions
+|- wrappers/                              Higher-Order components
+|- App.jsx                                App component
+|- index.jsx                              Root JS file (ReactDOM renderer)
+
+

Local Development Environment

+

The frontend inside the docker containers does not hot-reload, so +you need to use CRA dev server on your host machine to serve pages when doing development on the frontend, using docker nginx only as API source.

+
    +
  • +

    Start IntelOwl containers (see docs). Original dockerized app is accessible on http://localhost:80

    +
  • +
  • +

    If you have not node-js installed, you have to do that. Follow the guide here. We tested this with NodeJS >=16.6

    +
  • +
  • +

    Install npm packages locally

    +
  • +
+
cd ./frontend && npm install
+
+
    +
  • Start CRA dev server:
  • +
+
npm start
+
+
    +
  • +

    Now you can access the auto-reloading frontend on http://localhost:3000. It acts as proxy for API requests to original app web server.

    +
  • +
  • +

    JS app main configs are available in package.json.

    +
  • +
  • +

    (optional) Use local build of certego-ui package so it can also hot-reload. This is useful when you want to make changes in certego-ui and rapidly test them with IntelOwl. Refer here for setup instructions.

    +
  • +
+

Miscellaneous

+

Dependabot

+

We have dependabot enabled for the React.js frontend application. The updates are scheduled for once a week.

+

External Docs

+ +
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/frontend/jest.config.js b/Submodules/IntelOwl/frontend/jest.config.js new file mode 100644 index 0000000..4078b9c --- /dev/null +++ b/Submodules/IntelOwl/frontend/jest.config.js @@ -0,0 +1,230 @@ +/* + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/en/configuration.html + */ +const coverageReporters = [["text", { skipFull: true, skipEmpty: true }]]; +if (!process.env.CI) { + // show HTML coverage only locally + coverageReporters.push(["html", { skipFull: true, skipEmpty: true }]); +} + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls and instances between every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + collectCoverageFrom: ["src/**/*.js", "src/**/*.jsx"], + + // The directory where Jest should output its coverage files + coverageDirectory: ".coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: ["commons.js"], + + // Indicates which provider should be used to instrument code for coverage + // coverageProvider: "babel", + + // A list of reporter names that Jest uses when writing coverage reports + // ["json", {skipFull: true, skipEmpty: true}], + // "lcov", + // "clover" + // eslint-disable-next-line object-shorthand + coverageReporters: coverageReporters, + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + globals: { + COOKIE: ["foo", "bar"], + }, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + moduleDirectories: ["node_modules"], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + moduleNameMapper: { + // "\\.(css|scss)$": "/tests/styleMock", + "^certego(.*)$": "/src$1", + "^test-utils$": "/tests/testing-utils", + "^lodash-es$": "lodash", + "d3-(.*)$": "/node_modules/d3-$1/dist/d3-$1.min.js", + "react-markdown": + "/node_modules/react-markdown/react-markdown.min.js", + }, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + setupFilesAfterEnv: ["./tests/test-setup.js"], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "jsdom", + + // Options that will be passed to the testEnvironment + testEnvironmentOptions: { + url: "https://localhost", + }, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + testTimeout: 15 * 1000, + + // A map from regular expressions to paths to transformers + // transform: undefined, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + /* node_modules can need to be transformated in this case we have to exclude them from the regex + Simply add node_modules name to the list + */ + transformIgnorePatterns: [ + `/node_modules/(?!(${[ + "nanoid", + "react-markdown", + "vfile", + "vfile-message", + "markdown-table", + "unist-.*", + "unified", + "bail", + "is-plain-obj", + "trough", + "remark-.*", + "mdast-util-.*", + "escape-string-regexp", + "micromark.*", + "decode-named-character-reference", + "character-entities", + "property-information", + "hast-util-whitespace", + "space-separated-tokens", + "comma-separated-tokens", + "pretty-bytes", + "ccount", + ].join("|")})/)`, + "\\.pnp\\.[^\\/]+$", + ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/Submodules/IntelOwl/frontend/package-lock.json b/Submodules/IntelOwl/frontend/package-lock.json new file mode 100644 index 0000000..20a8b6d --- /dev/null +++ b/Submodules/IntelOwl/frontend/package-lock.json @@ -0,0 +1,41658 @@ +{ + "name": "intelowl", + "version": "6.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "intelowl", + "version": "6.0.0", + "dependencies": { + "@certego/certego-ui": "^0.1.13", + "@dagrejs/dagre": "^1.0.4", + "axios": "^1.6.0", + "axios-hooks": "^3.1.5", + "bootstrap": "^5.3.2", + "classnames": "^2.5.1", + "flag-icons": "^7.1.0", + "formik": "^2.4.5", + "js-cookie": "^3.0.5", + "md5": "^2.3.0", + "prop-types": "^15.8.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-error-boundary": "^4.0.11", + "react-google-recaptcha": "^2.1.0", + "react-icons": "^4.12.0", + "react-joyride": "^2.8.1", + "react-json-tree": "^0.18.0", + "react-markdown": "^8.0.7", + "react-router-dom": "^6.22.0", + "react-scripts": "^5.0.1", + "react-select": "^5.8.0", + "react-table": "^7.8.0", + "react-use": "^17.5.0", + "reactflow": "^11.10.4", + "reactstrap": "^9.2.1", + "recharts": "^2.12.6", + "zustand": "^4.5.2" + }, + "devDependencies": { + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.23.3", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/react": "^12.1.5", + "@testing-library/user-event": "^14.5.2", + "babel-eslint": "^10.1.0", + "babel-jest": "^29.7.0", + "eslint": "^8.48.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.5.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "prettier": "^3.2.5", + "sass": "^1.77.0", + "stylelint": "^14.9.1", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-standard-scss": "^4.0.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dependencies": { + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", + "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.9.tgz", + "integrity": "sha512-xdMkt39/nviO/4vpVdrEYPwXCsYIXSSAr6mC7WQsNIlGnuxKyKE7GZjalcnbSWiC4OXGNNN3UQPeHfjSC6sTDA==", + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "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/eslint-parser/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/@babel/generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "dependencies": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", + "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", + "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "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.22.5", + "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.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.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-decorators": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.7.tgz", + "integrity": "sha512-omXqPF7Onq4Bb7wHxXjM3jSMSJvUUbvDvmmds7KI5n9Cq6Ln5I05I1W2nRlRof1rGdiUxJrxwe285WF96XlBXQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/plugin-syntax-decorators": "^7.22.5" + }, + "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-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@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.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "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-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "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-decorators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz", + "integrity": "sha512-avpUOBS7IU6al8MmF1XpAyj9QYeLPuSDJI5D4pVMSMdL7xQokKqJPYQC67RCT0aCTashUXPiGwMJ0DEXXCEmMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.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-flow": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", + "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "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.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "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-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "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-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", + "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-flow-strip-types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", + "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-flow": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-numeric-separator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "dependencies": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@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-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", + "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", + "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", + "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", + "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz", + "integrity": "sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", + "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@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.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@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-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "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 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", + "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-react-display-name": "^7.23.3", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz", + "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-typescript": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "node_modules/@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "node_modules/@certego/certego-ui": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@certego/certego-ui/-/certego-ui-0.1.13.tgz", + "integrity": "sha512-uTxPljCe+cw/ZMwsV06t8RE2iQ/RyTkJKTym3nrcPY5MCyGxiMtUlbtfrLKxZZCV8f82ojtYEJMBaymfDgF84w==", + "dependencies": { + "classnames": "^2.3.1", + "date-fns": "^2.28.0", + "match-sorter": "^6.3.1", + "nanoid": "^3.3.4", + "prop-types": "^15.8.1", + "react-compound-slider": "^3.3.1", + "react-icons": "^4.3.1", + "react-infinite-scroll-component": "^6.1.0", + "react-json-editor-ajrm": "^2.5.13", + "react-json-view": "^1.21.3", + "react-select": "^5.3.2", + "react-share": "^4.4.0", + "react-top-loading-bar": "^2.1.0", + "react-use": "^17.4.0", + "recharts": "^2.1.10", + "zustand": "^4.0.0-rc.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "axios": "^1.6.0", + "axios-hooks": "^3.0.4", + "bootstrap": "^5.2.1", + "formik": "^2.2.9", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-router-dom": "^6.3.0", + "react-table": "^7.8.0", + "reactstrap": "^9.0.3" + } + }, + "node_modules/@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@dagrejs/dagre": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.0.4.tgz", + "integrity": "sha512-jrEore+HhW1yg1Rsd9H1PPMcoEOD4bVh0WCXc6GqzyzubnJj4GaWGg8ETOrskTd/3n/g5LOzumGM4CCgpNLJNw==", + "dependencies": { + "@dagrejs/graphlib": "2.1.13" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.1.13.tgz", + "integrity": "sha512-calbMa7Gcyo+/t23XBaqQqon8LlgE9regey4UVoikoenKBXvUnCUL3s9RP6USCxttfr0XWVICtYUuKMdehKqMw==", + "engines": { + "node": ">17.0.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.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.2.0" + } + }, + "node_modules/@emotion/babel-plugin/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/@emotion/babel-plugin/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/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz", + "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==" + }, + "node_modules/@floating-ui/dom": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.5.tgz", + "integrity": "sha512-96KnRWkRnuBSSFbj0sFGwwOUd8EkiecINVl0O9wiZlZ64EkpyAOG3Xc2vKKNJmru0Z7RqWNymA+6b8OZqjgyyw==", + "dependencies": { + "@floating-ui/core": "^1.3.1" + } + }, + "node_modules/@gilbarbara/deep-equal": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", + "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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==" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/@jest/console/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/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==", + "dev": true + }, + "node_modules/@jest/console/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/@jest/core/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/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==", + "dev": true + }, + "node_modules/@jest/core/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/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==", + "dev": true + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/@jest/reporters/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "devOptional": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/@jest/transform/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/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==", + "dev": true + }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/@jest/transform/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "devOptional": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/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==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": 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/@jest/types/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==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/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==", + "devOptional": true + }, + "node_modules/@jest/types/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==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/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==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "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/@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/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/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/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/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/@nicolo-ribaudo/eslint-scope-5-internals/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/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", + "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "dependencies": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "@types/webpack": "4.x || 5.x", + "react-refresh": ">=0.10.0 <1.0.0", + "sockjs-client": "^1.4.0", + "type-fest": ">=0.17.0 <4.0.0", + "webpack": ">=4.43.0 <6.0.0", + "webpack-dev-server": "3.x || 4.x", + "webpack-hot-middleware": "2.x", + "webpack-plugin-serve": "0.x || 1.x" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + }, + "sockjs-client": { + "optional": true + }, + "type-fest": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + }, + "webpack-hot-middleware": { + "optional": true + }, + "webpack-plugin-serve": { + "optional": true + } + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pmmmwh/react-refresh-webpack-plugin/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@reactflow/background": { + "version": "11.3.9", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.9.tgz", + "integrity": "sha512-byj/G9pEC8tN0wT/ptcl/LkEP/BBfa33/SvBkqE4XwyofckqF87lKp573qGlisfnsijwAbpDlf81PuFL41So4Q==", + "dependencies": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.9", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.9.tgz", + "integrity": "sha512-e8nWplbYfOn83KN1BrxTXS17+enLyFnjZPbyDgHSRLtI5ZGPKF/8iRXV+VXb2LFVzlu4Wh3la/pkxtfP/0aguA==", + "dependencies": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.10.4.tgz", + "integrity": "sha512-j3i9b2fsTX/sBbOm+RmNzYEFWbNx4jGWGuGooh2r1jQaE2eV+TLJgiG/VNOp0q5mBl9f6g1IXs3Gm86S9JfcGw==", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.9", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.9.tgz", + "integrity": "sha512-le95jyTtt3TEtJ1qa7tZ5hyM4S7gaEQkW43cixcMOZLu33VAdc2aCpJg/fXcRrrf7moN2Mbl9WIMNXUKsp5ILA==", + "dependencies": { + "@reactflow/core": "11.10.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.9.tgz", + "integrity": "sha512-HfickMm0hPDIHt9qH997nLdgLt0kayQyslKE0RS/GZvZ4UMQJlx/NRRyj5y47Qyg0NnC66KYOQWDM9LLzRTnUg==", + "dependencies": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.9.tgz", + "integrity": "sha512-VmgxKmToax4sX1biZ9LXA7cj/TBJ+E5cklLGwquCCVVxh+lxpZGTBF3a5FJGVHiUNBBtFsC8ldcSZIK4cAlQww==", + "dependencies": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@remix-run/router": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", + "integrity": "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-node-resolve/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/pluginutils/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz", + "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "devOptional": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "dependencies": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/core/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "dependencies": { + "@babel/types": "^7.12.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "dependencies": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-svgo/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@testing-library/dom/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/@testing-library/dom/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==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/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==", + "dev": true, + "peer": true + }, + "node_modules/@testing-library/dom/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==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/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==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz", + "integrity": "sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.3.2", + "@babel/runtime": "^7.9.2", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + }, + "peerDependencies": { + "@jest/globals": ">= 28", + "@types/bun": "latest", + "@types/jest": ">= 28", + "jest": ">= 28", + "vitest": ">= 0.32" + }, + "peerDependenciesMeta": { + "@jest/globals": { + "optional": true + }, + "@types/bun": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "jest": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@testing-library/jest-dom/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/jest-dom/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==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", + "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "<18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "<18.0.0", + "react-dom": "<18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/react/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/react/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/@testing-library/react/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/react/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==", + "dev": true + }, + "node_modules/@testing-library/react/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/base16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.4.tgz", + "integrity": "sha512-Lt9Urpmkd0SS1EqmTkkUhaQD5jMItImRxsvVwzy4lVKPCI67rH7udDUxR8KrZvntlqSXTj833YXiHrU2y658Sw==" + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", + "integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz", + "integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", + "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.5.tgz", + "integrity": "sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", + "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "node_modules/@types/lodash": { + "version": "4.14.200", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", + "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==" + }, + "node_modules/@types/mdast": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", + "integrity": "sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "20.4.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz", + "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": 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/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" + }, + "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/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "node_modules/@types/react": { + "version": "18.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.16.tgz", + "integrity": "sha512-LLFWr12ZhBJ4YVw7neWLe6Pk7Ey5R9OCydfuMsz1L8bZxzaawJj2p06Q8/EFEHDeTBQNFLF62X+CG7B2zIyu0Q==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz", + "integrity": "sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==", + "dev": true, + "dependencies": { + "@types/react": "^17" + } + }, + "node_modules/@types/react-dom/node_modules/@types/react": { + "version": "17.0.62", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.62.tgz", + "integrity": "sha512-eANCyz9DG8p/Vdhr0ZKST8JV12PhH2ACCDYlFw6DIO+D+ca+uP4jtEDEpVqXZrh/uZdXQGwk7whJa3ah5DtyLw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" + }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" + }, + "node_modules/@types/unist": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", + "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==" + }, + "node_modules/@types/ws": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", + "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "devOptional": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "dependencies": { + "@typescript-eslint/utils": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/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/@typescript-eslint/utils/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/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "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==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peerDependencies": { + "acorn": "^8" + } + }, + "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==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "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-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "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==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "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/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", + "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.1.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios-hooks": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/axios-hooks/-/axios-hooks-3.1.5.tgz", + "integrity": "sha512-mU4WZ9c6YiOTxgTIKbswoHvb/b9fWXa2FxNFKI/hVcfD9Qemz1r9KLfRSVZf1GZg8nFry7oTM5gxNmPSn5PG0Q==", + "dependencies": { + "@babel/runtime": "7.18.9", + "dequal": "2.0.3", + "lru-cache": "6.0.0" + }, + "peerDependencies": { + "axios": ">=0.24.0", + "react": "^16.8.0-0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/axios-hooks/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/axios-hooks/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/axios-hooks/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "deprecated": "babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "eslint": ">= 4.12.1" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/babel-jest/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/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==", + "dev": true + }, + "node_modules/babel-jest/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "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-loader/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==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/babel-loader/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==", + "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/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.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-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "peerDependencies": { + "@babel/core": "^7.1.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.31.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "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==" + }, + "node_modules/bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "dependencies": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "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==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "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.11.0", + "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==", + "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==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.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==" + }, + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "node_modules/bootstrap": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", + "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "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==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "node_modules/browserslist": { + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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==" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "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/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "engines": { + "node": ">=4" + } + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "engines": { + "node": "*" + } + }, + "node_modules/check-types": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", + "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" + }, + "node_modules/classcat": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.4.tgz", + "integrity": "sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "dependencies": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==" + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "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==", + "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==", + "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==" + }, + "node_modules/compression/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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "engines": { + "node": ">=0.8" + } + }, + "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==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "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==" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", + "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-compat": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", + "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "dependencies": { + "browserslist": "^4.21.9" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-js-pure": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.31.1.tgz", + "integrity": "sha512-w+C62kvWti0EPs4KPMCMVv9DriHSXfQOCQ94bGGBiEW5rrbtt/Rz8n5Krhfw9cpFyzXBjf3DB3QnPdEzGDY4Fw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-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==" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "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-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/create-jest/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/create-jest/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==", + "dev": true + }, + "node_modules/create-jest/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/create-jest/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "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==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "engines": { + "node": "*" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-functions-list": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.0.tgz", + "integrity": "sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg==", + "dev": true, + "engines": { + "node": ">=12.22" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-in-js-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", + "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "dependencies": { + "hyphenate-style-name": "^1.0.3" + } + }, + "node_modules/css-loader": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", + "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.21", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.3", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/css-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "dependencies": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@parcel/css": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/css-minimizer-webpack-plugin/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/css-minimizer-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "node_modules/cssdb": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz", + "integrity": "sha512-1hN+I3r4VqSNQ+OmMXxYexnumbOONkSil0TWMebVXHtzYW4tRRPovUNHPHj2d4nrgOuYJ8Vs3XwvywsuwwXNNA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ] + }, + "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==", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "dependencies": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "dependencies": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "dependencies": { + "css-tree": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "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-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "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-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "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-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "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/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "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/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/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/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "node_modules/deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, + "node_modules/deep-equal": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==" + }, + "node_modules/deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "dependencies": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "bin": { + "detect": "bin/detect-port", + "detect-port": "bin/detect-port" + }, + "engines": { + "node": ">= 4.2.1" + } + }, + "node_modules/detect-port-alt/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/detect-port-alt/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "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==" + }, + "node_modules/dns-packet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", + "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dependencies": { + "utila": "~0.4" + } + }, + "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-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "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==" + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.471", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.471.tgz", + "integrity": "sha512-GpmGRC1vTl60w/k6YpQ18pSiqnmr0j3un//5TV1idPi6aheNfkT1Ye71tMEabWyNDO6sBMgAR+95Eb0eUUr1tA==" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "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/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "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==", + "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/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==" + }, + "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/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "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.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "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.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "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-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + }, + "engines": { + "node": "^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.28.0", + "eslint-plugin-react-hooks": "^4.3.0" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "dependencies": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "dependencies": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@babel/plugin-syntax-flow": "^7.14.5", + "@babel/plugin-transform-react-jsx": "^7.14.9", + "eslint": "^8.1.0" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^4.0.0 || ^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.34.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", + "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlast": "^1.2.4", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.3", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.17", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7", + "object.hasown": "^1.1.3", + "object.values": "^1.1.7", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.10" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dependencies": { + "is-core-module": "^2.13.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/eslint-plugin-testing-library": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz", + "integrity": "sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==", + "dependencies": { + "@typescript-eslint/utils": "^5.58.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0", + "npm": ">=6" + }, + "peerDependencies": { + "eslint": "^7.5.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/eslint-webpack-plugin/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/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/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==", + "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==", + "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==" + }, + "node_modules/eslint/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/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "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/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/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "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.11.0", + "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==" + }, + "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==", + "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==" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "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-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==" + }, + "node_modules/fast-loops": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz", + "integrity": "sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==" + }, + "node_modules/fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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==", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "dependencies": { + "fbjs": "^3.0.0" + } + }, + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "dependencies": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "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==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "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==", + "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==", + "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==", + "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==" + }, + "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==", + "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-cache-dir/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==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/flag-icons": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.1.0.tgz", + "integrity": "sha512-AH4v++19bpC5P3Wh767top4wylJYJCWkFnvNiDqGHDxqSqdMZ49jpLXp8PWBHTTXaNQ+/A+QPrOwyiIGaiIhmw==" + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, + "node_modules/flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "dependencies": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + }, + "peerDependencies": { + "react": "^15.0.2 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "engines": { + "node": ">=10", + "yarn": ">=1.0.0" + }, + "peerDependencies": { + "eslint": ">= 6", + "typescript": ">= 2.7", + "vue-template-compiler": "*", + "webpack": ">= 4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + } + } + }, + "node_modules/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/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/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dependencies": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formik": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.5.tgz", + "integrity": "sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==", + "funding": [ + { + "type": "individual", + "url": "https://opencollective.com/formik" + } + ], + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==" + }, + "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==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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": "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/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "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/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==" + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "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==", + "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.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "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.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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/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/hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "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==", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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/hpack.js/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/hpack.js/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/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/html-webpack-plugin": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", + "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "dependencies": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" + }, + "peerDependencies": { + "webpack": "^5.20.0" + } + }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "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==" + }, + "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==", + "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==" + }, + "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==", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, + "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.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/immutable": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", + "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==", + "devOptional": 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-fresh/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/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "node_modules/inline-style-prefixer": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.0.tgz", + "integrity": "sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ==", + "dependencies": { + "css-in-js-utils": "^3.1.0", + "fast-loops": "^1.1.3" + } + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "engines": { + "node": ">= 10" + } + }, + "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-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "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-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "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==", + "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==", + "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==" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-lite": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz", + "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==" + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "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==", + "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-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/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/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/jake/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/jake/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/jake/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-circus/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true + }, + "node_modules/jest-circus/node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/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==", + "dev": true + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-cli/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/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==", + "dev": true + }, + "node_modules/jest-cli/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-config/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/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==", + "dev": true + }, + "node_modules/jest-config/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/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==", + "dev": true + }, + "node_modules/jest-config/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-diff/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true + }, + "node_modules/jest-diff/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/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==", + "dev": true + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-each/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true + }, + "node_modules/jest-each/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/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==", + "dev": true + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "devOptional": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "devOptional": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/jest-jasmine2/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/jest-jasmine2/node_modules/@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-jasmine2/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/jest-jasmine2/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/jest-jasmine2/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/jest-jasmine2/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/jest-jasmine2/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-jasmine2/node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-jasmine2/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-jasmine2/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/jest-jasmine2/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/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==", + "dev": true + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-message-util/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true + }, + "node_modules/jest-message-util/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/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==", + "dev": true + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "devOptional": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "devOptional": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/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==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": 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/jest-resolve/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==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/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==", + "devOptional": true + }, + "node_modules/jest-resolve/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==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/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==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-runner/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true + }, + "node_modules/jest-runner/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-runtime/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true + }, + "node_modules/jest-runtime/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-snapshot/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "devOptional": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/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==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": 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/jest-util/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==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/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==", + "devOptional": true + }, + "node_modules/jest-util/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==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/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==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "devOptional": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/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==", + "devOptional": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "devOptional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": 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/jest-validate/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==", + "devOptional": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/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==", + "devOptional": true + }, + "node_modules/jest-validate/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==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "devOptional": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "devOptional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/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==", + "devOptional": true + }, + "node_modules/jest-validate/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==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": 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/jest-watcher/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true + }, + "node_modules/jest-watcher/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "devOptional": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/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==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "devOptional": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, + "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": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "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-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": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "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==" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", + "dependencies": { + "debug": "^2.1.3" + } + }, + "node_modules/jsonp/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/jsonp/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/known-css-properties": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", + "dev": true + }, + "node_modules/language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "engines": { + "node": ">=10" + } + }, + "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/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "engines": { + "node": ">=6.11.5" + } + }, + "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==", + "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==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + }, + "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.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + }, + "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==" + }, + "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==" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "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==" + }, + "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/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "node_modules/mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "dependencies": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "dependencies": { + "@types/mdast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^1.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "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==", + "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==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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==" + }, + "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.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "engines": { + "node": ">=4" + } + }, + "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": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nano-css": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.1.tgz", + "integrity": "sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "css-tree": "^1.1.2", + "csstype": "^3.1.2", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^7.0.0", + "rtl-css-js": "^1.16.1", + "stacktrace-js": "^2.0.2", + "stylis": "^4.3.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/nano-css/node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" + }, + "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==", + "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==" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "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-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/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/node-fetch/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/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "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==", + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, + "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-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "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.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", + "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "safe-array-concat": "^1.0.0" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/object.hasown": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "dependencies": { + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "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==", + "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==", + "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/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "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-locate/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-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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-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/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "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": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "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/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "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==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "engines": { + "node": ">= 6" + } + }, + "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==", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "browserslist": ">=4", + "postcss": ">=8" + } + }, + "node_modules/postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "dependencies": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.2.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "peerDependencies": { + "postcss": "^8.1.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "node_modules/postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "dependencies": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "dependencies": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "dependencies": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "browserslist": ">= 4", + "postcss": ">= 8" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "dependencies": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "dependencies": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "dependencies": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + } + ], + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.19" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-svgo/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/postcss-svgo/node_modules/svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "dependencies": { + "postcss-selector-parser": "^6.0.5" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "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==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "dependencies": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/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/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==" + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "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/property-information": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", + "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/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==", + "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/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "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==", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "dependencies": { + "performance-now": "^2.1.0" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "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==", + "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==", + "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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "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-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "dependencies": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-app-polyfill/node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "dependencies": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, + "node_modules/react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "dependencies": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "node_modules/react-compound-slider": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-3.4.0.tgz", + "integrity": "sha512-KSje/rB0xSvvcb7YV0+82hkiXTV5ljSS7axKrNiXLf9AEO+rrr1Xq4MJWA+6v030YNNo/RoSoEB6D6fnoy+8ng==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "d3-array": "^2.8.0", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.9" + } + }, + "node_modules/react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "dependencies": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/react-dev-utils/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/react-dev-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/react-dev-utils/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/react-dev-utils/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/react-dev-utils/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/react-dev-utils/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-dev-utils/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/react-dev-utils/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-error-boundary": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz", + "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "node_modules/react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "node_modules/react-floater": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz", + "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==", + "dependencies": { + "deepmerge": "^4.3.1", + "is-lite": "^0.8.2", + "popper.js": "^1.16.0", + "prop-types": "^15.8.1", + "tree-changes": "^0.9.1" + }, + "peerDependencies": { + "react": "15 - 18", + "react-dom": "15 - 18" + } + }, + "node_modules/react-floater/node_modules/@gilbarbara/deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==" + }, + "node_modules/react-floater/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-floater/node_modules/is-lite": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz", + "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==" + }, + "node_modules/react-floater/node_modules/tree-changes": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz", + "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==", + "dependencies": { + "@gilbarbara/deep-equal": "^0.1.1", + "is-lite": "^0.8.2" + } + }, + "node_modules/react-google-recaptcha": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.1.0.tgz", + "integrity": "sha512-K9jr7e0CWFigi8KxC3WPvNqZZ47df2RrMAta6KmRoE4RUi7Ys6NmNjytpXpg4HI/svmQJLKR+PncEPaNJ98DqQ==", + "dependencies": { + "prop-types": "^15.5.0", + "react-async-script": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16.4.1" + } + }, + "node_modules/react-icons": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-infinite-scroll-component": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", + "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", + "dependencies": { + "throttle-debounce": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.0.0" + } + }, + "node_modules/react-innertext": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz", + "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==", + "peerDependencies": { + "@types/react": ">=0.0.0 <=99", + "react": ">=0.0.0 <=99" + } + }, + "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/react-joyride": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.8.1.tgz", + "integrity": "sha512-fVwCmoOvJsiFKKHn8mvPUYc4JUUkgAsQMvarpZDtFPTc4duj240b12+AB8+3NXlTYGZVnKNSTgFFzoSh9RxjmQ==", + "dependencies": { + "@gilbarbara/deep-equal": "^0.3.1", + "deep-diff": "^1.0.2", + "deepmerge": "^4.3.1", + "is-lite": "^1.2.1", + "react-floater": "^0.7.9", + "react-innertext": "^1.1.5", + "react-is": "^16.13.1", + "scroll": "^3.0.1", + "scrollparent": "^2.1.0", + "tree-changes": "^0.11.2", + "type-fest": "^4.15.0" + }, + "peerDependencies": { + "react": "15 - 18", + "react-dom": "15 - 18" + } + }, + "node_modules/react-joyride/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-joyride/node_modules/type-fest": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.18.2.tgz", + "integrity": "sha512-+suCYpfJLAe4OXS6+PPXjW3urOS4IoP9waSiLuXfLgqZODKw/aWwASvzqE886wA0kQgGy0mIWyhd87VpqIy6Xg==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-json-editor-ajrm": { + "version": "2.5.14", + "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.14.tgz", + "integrity": "sha512-z2035l5lnU0Wo73AUonj68d1Eh9vjVcLXEa8YogDEeFPcogzYkfT6R38fvYlLKDhP0oI+LyOO/IBWsFkXs4M0Q==", + "dependencies": { + "@babel/runtime": "^7.0.0-rc.0" + }, + "peerDependencies": { + "react": ">=16.2.0", + "react-dom": ">=16.2.0" + } + }, + "node_modules/react-json-tree": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.18.0.tgz", + "integrity": "sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==", + "dependencies": { + "@babel/runtime": "^7.20.6", + "@types/lodash": "^4.14.191", + "react-base16-styling": "^0.9.1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-json-tree/node_modules/react-base16-styling": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz", + "integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==", + "dependencies": { + "@babel/runtime": "^7.16.7", + "@types/base16": "^1.0.2", + "@types/lodash": "^4.14.178", + "base16": "^1.0.0", + "color": "^3.2.1", + "csstype": "^3.0.10", + "lodash.curry": "^4.1.1" + } + }, + "node_modules/react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "dependencies": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^16.3.0 || ^15.5.4", + "react-dom": "^17.0.0 || ^16.3.0 || ^15.5.4" + } + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, + "node_modules/react-markdown/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-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "dependencies": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } + }, + "node_modules/react-popper/node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + }, + "node_modules/react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", + "integrity": "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==", + "dependencies": { + "@remix-run/router": "1.15.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", + "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", + "dependencies": { + "@remix-run/router": "1.15.0", + "react-router": "6.22.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "dependencies": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "bin": { + "react-scripts": "bin/react-scripts.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + }, + "peerDependencies": { + "react": ">= 16", + "typescript": "^3.2.1 || ^4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "dependencies": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "node_modules/react-scripts/node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/react-scripts/node_modules/@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/react-scripts/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-scripts/node_modules/@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/react-scripts/node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/react-scripts/node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/react-scripts/node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/react-scripts/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/react-scripts/node_modules/babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "dependencies": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/react-scripts/node_modules/babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "dependencies": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/react-scripts/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-scripts/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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/react-scripts/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/react-scripts/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/react-scripts/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/react-scripts/node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "node_modules/react-scripts/node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts/node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-scripts/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-scripts/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==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-scripts/node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "dependencies": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "dependencies": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "dependencies": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/react-scripts/node_modules/jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "dependencies": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "dependencies": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "dependencies": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "dependencies": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "dependencies": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "dependencies": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "dependencies": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "dependencies": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "dependencies": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "jest": "^27.0.0 || ^28.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/console/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-message-util/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-watcher/node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/jest-watcher/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/react-scripts/node_modules/jest-watch-typeahead/node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "dependencies": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/string-length/node_modules/char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watch-typeahead/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "dependencies": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/react-scripts/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/react-scripts/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/react-scripts/node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "node_modules/react-scripts/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-scripts/node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-scripts/node_modules/v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/react-scripts/node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/react-scripts/node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/react-scripts/node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/react-scripts/node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "node_modules/react-scripts/node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-scripts/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/react-scripts/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/react-scripts/node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "node_modules/react-scripts/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/react-scripts/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-share": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.1.tgz", + "integrity": "sha512-AJ9m9RiJssqvYg7MoJUc9J0D7b/liWrsfQ99ndKc5vJ4oVHHd4Fy87jBlKEQPibT40oYA3AQ/a9/oQY6/yaigw==", + "dependencies": { + "classnames": "^2.3.2", + "jsonp": "^0.2.1" + }, + "engines": { + "node": ">=6.9.0", + "npm": ">=5.0.0" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17 || ^18" + } + }, + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-table": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17.0.0-0 || ^18.0.0" + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz", + "integrity": "sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-top-loading-bar": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/react-top-loading-bar/-/react-top-loading-bar-2.3.1.tgz", + "integrity": "sha512-rQk2Nm+TOBrM1C4E3e6KwT65iXyRSgBHjCkr2FNja1S51WaPulRA5nKj/xazuQ3x89wDDdGsrqkqy0RBIfd0xg==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18" + } + }, + "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/react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "peerDependencies": { + "react": "*", + "tslib": "*" + } + }, + "node_modules/react-use": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.5.0.tgz", + "integrity": "sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==", + "dependencies": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.6.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/react-use/node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "node_modules/react-use/node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/reactflow": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.10.4.tgz", + "integrity": "sha512-0CApYhtYicXEDg/x2kvUHiUk26Qur8lAtTtiSlptNKuyEuGti6P1y5cS32YGaUoDMoCqkm/m+jcKkfMOvSCVRA==", + "dependencies": { + "@reactflow/background": "11.3.9", + "@reactflow/controls": "11.2.9", + "@reactflow/core": "11.10.4", + "@reactflow/minimap": "11.7.9", + "@reactflow/node-resizer": "2.2.9", + "@reactflow/node-toolbar": "1.3.9" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/reactstrap": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz", + "integrity": "sha512-4KroiGOdqZLAnMGzHjpErW3G7bLB+QbKzzMLIDXydPIV0y74lpdL7WtXHkLWAGInd97WCPNx4+R0NQDPyzIfhw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.6.0", + "classnames": "^2.2.3", + "prop-types": "^15.5.8", + "react-popper": "^2.2.4", + "react-transition-group": "^4.4.2" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/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/read-pkg/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/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.6.tgz", + "integrity": "sha512-D+7j9WI+D0NHauah3fKHuNNcRK8bOypPW7os1DERinogGBGaHI7i6tQKJ0aUF3JXyBZ63dyfKIW2WTOPJDxJ8w==", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "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/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, + "node_modules/renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "dependencies": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "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": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==" + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.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": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=8.9" + }, + "peerDependencies": { + "rework": "1.0.1", + "rework-visit": "1.0.0" + }, + "peerDependenciesMeta": { + "rework": { + "optional": true + }, + "rework-visit": { + "optional": true + } + } + }, + "node_modules/resolve-url-loader/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "node_modules/resolve-url-loader/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "devOptional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "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/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0" + } + }, + "node_modules/rollup-plugin-terser/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup-plugin-terser/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/rollup-plugin-terser/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rtl-css-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", + "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "node_modules/sass": { + "version": "1.77.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.0.tgz", + "integrity": "sha512-eGj4HNfXqBWtSnvItNkn7B6icqH14i3CiCGbzMKs3BAPTq62pp9NBYsBgyN4cA+qssqo9r26lW4JSvlaUUWbgw==", + "devOptional": 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": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "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": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scroll": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", + "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==" + }, + "node_modules/scrollparent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz", + "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==" + }, + "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==" + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "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==", + "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==" + }, + "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==" + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "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==", + "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==", + "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==", + "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==", + "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==" + }, + "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==" + }, + "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==" + }, + "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==", + "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==", + "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/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==", + "engines": { + "node": ">=6.9" + } + }, + "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==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "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==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "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==" + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/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==", + "dev": true + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "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==" + }, + "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/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-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "dependencies": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead" + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "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.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "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==", + "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==", + "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/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" + }, + "node_modules/stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "node_modules/stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "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/string-width/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/string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-loader": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", + "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true + }, + "node_modules/style-to-object": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz", + "integrity": "sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==", + "dependencies": { + "inline-style-parser": "0.1.1" + } + }, + "node_modules/stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "dependencies": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >=14.0" + }, + "peerDependencies": { + "postcss": "^8.2.15" + } + }, + "node_modules/stylelint": { + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", + "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^7.1.0", + "css-functions-list": "^3.1.0", + "debug": "^4.3.4", + "fast-glob": "^3.2.12", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^6.0.1", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.1", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.26.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.19", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.3.0", + "svg-tags": "^1.0.0", + "table": "^6.8.1", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.2" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-prettier": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz", + "integrity": "sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==", + "dev": true, + "bin": { + "stylelint-config-prettier": "bin/check.js", + "stylelint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">= 11.x < 15" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-recommended-scss": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-6.0.0.tgz", + "integrity": "sha512-6QOe2/OzXV2AP5FE12A7+qtKdZik7Saf42SMMl84ksVBBPpTdrV+9HaCbPYiRMiwELY9hXCVdH4wlJ+YJb5eig==", + "dev": true, + "dependencies": { + "postcss-scss": "^4.0.2", + "stylelint-config-recommended": "^7.0.0", + "stylelint-scss": "^4.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^7.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-standard-scss": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-4.0.0.tgz", + "integrity": "sha512-xizu8PTEyB6zYXBiVg6VtvUYn9m57x+6ZtaOdaxsfpbe5eagLPGNlbYnKfm/CfN69ArUpnwR6LjgsTHzlGbtXQ==", + "dev": true, + "dependencies": { + "stylelint-config-recommended-scss": "^6.0.0", + "stylelint-config-standard": "^25.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-scss": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.7.0.tgz", + "integrity": "sha512-TSUgIeS0H3jqDZnby1UO1Qv3poi1N8wUYIJY6D1tuUq2MN3lwp/rITVo0wD+1SWTmRm0tNmGO0b7nKInnqF6Hg==", + "dev": true, + "dependencies": { + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "stylelint": "^14.5.1 || ^15.0.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "deprecated": "This SVGO version is no longer supported. Upgrade to v2.x.x.", + "dependencies": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/svgo/node_modules/css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "node_modules/svgo/node_modules/css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dependencies": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/svgo/node_modules/css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/svgo/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/svgo/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/svgo/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/svgo/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "node_modules/svgo/node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tailwindcss": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", + "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/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/terser/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" + }, + "node_modules/throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "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/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "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-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==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "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==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tree-changes": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.2.tgz", + "integrity": "sha512-4gXlUthrl+RabZw6lLvcCDl6KfJOCmrC16BC5CRdut1EAH509Omgg0BfKLY+ViRlzrvYOTWR0FMS2SQTwzumrw==", + "dependencies": { + "@gilbarbara/deep-equal": "^0.3.1", + "is-lite": "^1.2.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "node_modules/ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/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==", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "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==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "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==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz", + "integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "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==", + "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/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.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "dependencies": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "dependencies": { + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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/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==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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-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.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "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==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "dependencies": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "bin": { + "uvu": "bin.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uvu/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": 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==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/victory-vendor": { + "version": "36.6.11", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.11.tgz", + "integrity": "sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg==", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-vendor/node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.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==", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "dependencies": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "webpack": "^4.44.2 || ^5.47.0" + } + }, + "node_modules/webpack-manifest-plugin/node_modules/webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "dependencies": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/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/webpack/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/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "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==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.17", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", + "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==" + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "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-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/workbox-background-sync": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", + "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", + "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-build": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", + "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.6.0", + "workbox-broadcast-update": "6.6.0", + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-google-analytics": "6.6.0", + "workbox-navigation-preload": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-range-requests": "6.6.0", + "workbox-recipes": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0", + "workbox-streams": "6.6.0", + "workbox-sw": "6.6.0", + "workbox-window": "6.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", + "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", + "deprecated": "workbox-background-sync@6.6.0", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", + "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==" + }, + "node_modules/workbox-expiration": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", + "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-google-analytics": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", + "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", + "dependencies": { + "workbox-background-sync": "6.6.0", + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", + "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-precaching": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", + "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", + "dependencies": { + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "node_modules/workbox-range-requests": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", + "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-recipes": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", + "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", + "dependencies": { + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "node_modules/workbox-routing": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", + "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-strategies": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", + "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", + "dependencies": { + "workbox-core": "6.6.0" + } + }, + "node_modules/workbox-streams": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", + "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", + "dependencies": { + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0" + } + }, + "node_modules/workbox-sw": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", + "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==" + }, + "node_modules/workbox-webpack-plugin": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", + "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", + "dependencies": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.6.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "webpack": "^4.4.0 || ^5.9.0" + } + }, + "node_modules/workbox-webpack-plugin/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==", + "dependencies": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "node_modules/workbox-window": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", + "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.6.0" + } + }, + "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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/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": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "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/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "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": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + } + }, + "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==" + }, + "@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true + }, + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" + }, + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "requires": { + "@babel/highlight": "^7.22.5" + } + }, + "@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==" + }, + "@babel/core": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", + "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.1" + } + }, + "@babel/eslint-parser": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.9.tgz", + "integrity": "sha512-xdMkt39/nviO/4vpVdrEYPwXCsYIXSSAr6mC7WQsNIlGnuxKyKE7GZjalcnbSWiC4OXGNNN3UQPeHfjSC6sTDA==", + "requires": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "dependencies": { + "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==" + } + } + }, + "@babel/generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "requires": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", + "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", + "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==" + }, + "@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "requires": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "requires": { + "@babel/types": "^7.22.15" + } + }, + "@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==" + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==" + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" + }, + "@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==" + }, + "@babel/helper-wrap-function": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", + "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", + "requires": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/helpers": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "requires": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" + } + }, + "@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "requires": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.5", + "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.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" + } + }, + "@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-decorators": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.7.tgz", + "integrity": "sha512-omXqPF7Onq4Bb7wHxXjM3jSMSJvUUbvDvmmds7KI5n9Cq6Ln5I05I1W2nRlRof1rGdiUxJrxwe285WF96XlBXQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/plugin-syntax-decorators": "^7.22.5" + } + }, + "@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-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@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.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "requires": {} + }, + "@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-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "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-decorators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz", + "integrity": "sha512-avpUOBS7IU6al8MmF1XpAyj9QYeLPuSDJI5D4pVMSMdL7xQokKqJPYQC67RCT0aCTashUXPiGwMJ0DEXXCEmMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.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-flow": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", + "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-attributes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@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.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@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-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-async-generator-functions": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", + "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", + "requires": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-class-static-block": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dynamic-import": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-export-namespace-from": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", + "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-flow": "^7.22.5" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "requires": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-json-strings": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "requires": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-transform-numeric-separator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-transform-object-rest-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", + "requires": { + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.5" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + } + }, + "@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-private-property-in-object": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-constant-elements": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz", + "integrity": "sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", + "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", + "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/types": "^7.23.4" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "requires": { + "@babel/plugin-transform-react-jsx": "^7.22.5" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", + "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", + "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", + "requires": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "semver": "^6.3.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz", + "integrity": "sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-property-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/preset-env": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", + "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@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.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@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-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + } + }, + "@babel/preset-modules": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "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.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", + "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-react-display-name": "^7.23.3", + "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.23.3" + } + }, + "@babel/preset-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz", + "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-typescript": "^7.22.5" + } + }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" + }, + "@babel/runtime": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", + "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "requires": { + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } + } + }, + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + }, + "@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "requires": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" + }, + "@certego/certego-ui": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@certego/certego-ui/-/certego-ui-0.1.13.tgz", + "integrity": "sha512-uTxPljCe+cw/ZMwsV06t8RE2iQ/RyTkJKTym3nrcPY5MCyGxiMtUlbtfrLKxZZCV8f82ojtYEJMBaymfDgF84w==", + "requires": { + "classnames": "^2.3.1", + "date-fns": "^2.28.0", + "match-sorter": "^6.3.1", + "nanoid": "^3.3.4", + "prop-types": "^15.8.1", + "react-compound-slider": "^3.3.1", + "react-icons": "^4.3.1", + "react-infinite-scroll-component": "^6.1.0", + "react-json-editor-ajrm": "^2.5.13", + "react-json-view": "^1.21.3", + "react-select": "^5.3.2", + "react-share": "^4.4.0", + "react-top-loading-bar": "^2.1.0", + "react-use": "^17.4.0", + "recharts": "^2.1.10", + "zustand": "^4.0.0-rc.0" + } + }, + "@csstools/normalize.css": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz", + "integrity": "sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==" + }, + "@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "requires": {} + }, + "@dagrejs/dagre": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.0.4.tgz", + "integrity": "sha512-jrEore+HhW1yg1Rsd9H1PPMcoEOD4bVh0WCXc6GqzyzubnJj4GaWGg8ETOrskTd/3n/g5LOzumGM4CCgpNLJNw==", + "requires": { + "@dagrejs/graphlib": "2.1.13" + } + }, + "@dagrejs/graphlib": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.1.13.tgz", + "integrity": "sha512-calbMa7Gcyo+/t23XBaqQqon8LlgE9regey4UVoikoenKBXvUnCUL3s9RP6USCxttfr0XWVICtYUuKMdehKqMw==" + }, + "@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.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.2.0" + }, + "dependencies": { + "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==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, + "@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "requires": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/react": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.2", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", + "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "requires": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "requires": { + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==" + } + } + }, + "@eslint-community/regexpp": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==" + }, + "@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.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": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "requires": { + "type-fest": "^0.20.2" + } + }, + "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" + } + }, + "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==" + } + } + }, + "@eslint/js": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==" + }, + "@floating-ui/core": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz", + "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==" + }, + "@floating-ui/dom": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.5.tgz", + "integrity": "sha512-96KnRWkRnuBSSFbj0sFGwwOUd8EkiecINVl0O9wiZlZ64EkpyAOG3Xc2vKKNJmru0Z7RqWNymA+6b8OZqjgyyw==", + "requires": { + "@floating-ui/core": "^1.3.1" + } + }, + "@gilbarbara/deep-equal": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.3.1.tgz", + "integrity": "sha512-I7xWjLs2YSVMc5gGx1Z3ZG1lgFpITPndpi8Ku55GeEIKpACCPQNS/OTqQbxgTCfq0Ncvcc+CrFov96itVh6Qvw==" + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" + }, + "@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==" + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==" + }, + "@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + } + }, + "@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "requires": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + } + }, + "@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "devOptional": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.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==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "devOptional": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": 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==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@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/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@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==" + } + } + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" + }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "requires": { + "eslint-scope": "5.1.1" + }, + "dependencies": { + "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" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pmmmwh/react-refresh-webpack-plugin": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", + "integrity": "sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA==", + "requires": { + "ansi-html-community": "^0.0.8", + "common-path-prefix": "^3.0.0", + "core-js-pure": "^3.23.3", + "error-stack-parser": "^2.0.6", + "find-up": "^5.0.0", + "html-entities": "^2.1.0", + "loader-utils": "^2.0.4", + "schema-utils": "^3.0.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" + }, + "@reactflow/background": { + "version": "11.3.9", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.9.tgz", + "integrity": "sha512-byj/G9pEC8tN0wT/ptcl/LkEP/BBfa33/SvBkqE4XwyofckqF87lKp573qGlisfnsijwAbpDlf81PuFL41So4Q==", + "requires": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + } + }, + "@reactflow/controls": { + "version": "11.2.9", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.9.tgz", + "integrity": "sha512-e8nWplbYfOn83KN1BrxTXS17+enLyFnjZPbyDgHSRLtI5ZGPKF/8iRXV+VXb2LFVzlu4Wh3la/pkxtfP/0aguA==", + "requires": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + } + }, + "@reactflow/core": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.10.4.tgz", + "integrity": "sha512-j3i9b2fsTX/sBbOm+RmNzYEFWbNx4jGWGuGooh2r1jQaE2eV+TLJgiG/VNOp0q5mBl9f6g1IXs3Gm86S9JfcGw==", + "requires": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + } + }, + "@reactflow/minimap": { + "version": "11.7.9", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.9.tgz", + "integrity": "sha512-le95jyTtt3TEtJ1qa7tZ5hyM4S7gaEQkW43cixcMOZLu33VAdc2aCpJg/fXcRrrf7moN2Mbl9WIMNXUKsp5ILA==", + "requires": { + "@reactflow/core": "11.10.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + } + }, + "@reactflow/node-resizer": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.9.tgz", + "integrity": "sha512-HfickMm0hPDIHt9qH997nLdgLt0kayQyslKE0RS/GZvZ4UMQJlx/NRRyj5y47Qyg0NnC66KYOQWDM9LLzRTnUg==", + "requires": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + } + }, + "@reactflow/node-toolbar": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.9.tgz", + "integrity": "sha512-VmgxKmToax4sX1biZ9LXA7cj/TBJ+E5cklLGwquCCVVxh+lxpZGTBF3a5FJGVHiUNBBtFsC8ldcSZIK4cAlQww==", + "requires": { + "@reactflow/core": "11.10.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + } + }, + "@remix-run/router": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", + "integrity": "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==" + }, + "@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, + "@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "dependencies": { + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + } + } + }, + "@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "requires": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + } + } + }, + "@rushstack/eslint-patch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz", + "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==" + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "devOptional": true + }, + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "requires": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "@svgr/babel-plugin-add-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==" + }, + "@svgr/babel-plugin-remove-jsx-attribute": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz", + "integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==" + }, + "@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz", + "integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==" + }, + "@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz", + "integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==" + }, + "@svgr/babel-plugin-svg-dynamic-title": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz", + "integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==" + }, + "@svgr/babel-plugin-svg-em-dimensions": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz", + "integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==" + }, + "@svgr/babel-plugin-transform-react-native-svg": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz", + "integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==" + }, + "@svgr/babel-plugin-transform-svg-component": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz", + "integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==" + }, + "@svgr/babel-preset": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz", + "integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==", + "requires": { + "@svgr/babel-plugin-add-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^5.0.1", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^5.0.1", + "@svgr/babel-plugin-svg-dynamic-title": "^5.4.0", + "@svgr/babel-plugin-svg-em-dimensions": "^5.4.0", + "@svgr/babel-plugin-transform-react-native-svg": "^5.4.0", + "@svgr/babel-plugin-transform-svg-component": "^5.5.0" + } + }, + "@svgr/core": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz", + "integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==", + "requires": { + "@svgr/plugin-jsx": "^5.5.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + } + } + }, + "@svgr/hast-util-to-babel-ast": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz", + "integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==", + "requires": { + "@babel/types": "^7.12.6" + } + }, + "@svgr/plugin-jsx": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz", + "integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==", + "requires": { + "@babel/core": "^7.12.3", + "@svgr/babel-preset": "^5.5.0", + "@svgr/hast-util-to-babel-ast": "^5.5.0", + "svg-parser": "^2.0.2" + } + }, + "@svgr/plugin-svgo": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz", + "integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==", + "requires": { + "cosmiconfig": "^7.0.0", + "deepmerge": "^4.2.2", + "svgo": "^1.2.2" + }, + "dependencies": { + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + } + } + }, + "@svgr/webpack": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz", + "integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/plugin-transform-react-constant-elements": "^7.12.1", + "@babel/preset-env": "^7.12.1", + "@babel/preset-react": "^7.12.5", + "@svgr/core": "^5.5.0", + "@svgr/plugin-jsx": "^5.5.0", + "@svgr/plugin-svgo": "^5.5.0", + "loader-utils": "^2.0.0" + } + }, + "@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "peer": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "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==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/jest-dom": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz", + "integrity": "sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.3.2", + "@babel/runtime": "^7.9.2", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/react": { + "version": "12.1.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", + "integrity": "sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "<18.0.0" + }, + "dependencies": { + "@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "requires": {} + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" + }, + "@types/aria-query": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/base16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/base16/-/base16-1.0.4.tgz", + "integrity": "sha512-Lt9Urpmkd0SS1EqmTkkUhaQD5jMItImRxsvVwzy4lVKPCI67rH7udDUxR8KrZvntlqSXTj833YXiHrU2y658Sw==" + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", + "integrity": "sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "requires": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "@types/d3-array": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.5.tgz", + "integrity": "sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A==" + }, + "@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==" + }, + "@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "requires": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==" + }, + "@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==" + }, + "@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "requires": { + "@types/d3-dsv": "*" + } + }, + "@types/d3-force": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.9.tgz", + "integrity": "sha512-IKtvyFdb4Q0LWna6ymywQsEYjK/94SGhPrMfEr1TIc5OBeziTi+1jcCvttts8e0UWZIxpasjnQk9MNk/3iS+kA==" + }, + "@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "requires": { + "@types/geojson": "*" + } + }, + "@types/d3-hierarchy": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.6.tgz", + "integrity": "sha512-qlmD/8aMk5xGorUvTUWHCiumvgaUXYldYjNVOWtYoTYY/L+WwIEAmJxUmTgr9LoGNG0PPAOmqMDJVDPc7DOpPw==" + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==" + }, + "@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "@types/d3-scale": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.3.tgz", + "integrity": "sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", + "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" + }, + "@types/d3-selection": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.10.tgz", + "integrity": "sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==" + }, + "@types/d3-shape": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.1.tgz", + "integrity": "sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==" + }, + "@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==" + }, + "@types/d3-transition": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.8.tgz", + "integrity": "sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "requires": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "requires": { + "@types/ms": "*" + } + }, + "@types/eslint": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", + "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + }, + "@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "requires": { + "@types/node": "*" + } + }, + "@types/hast": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.5.tgz", + "integrity": "sha512-SvQi0L/lNpThgPoleH53cdjB3y9zpLlVjRbqB3rH8hx1jiRSBGAhyjV3H+URFjNVRqt2EdYNrbZE5IsGlNfpRg==", + "requires": { + "@types/unist": "^2" + } + }, + "@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==" + }, + "@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" + }, + "@types/http-proxy": { + "version": "1.17.11", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", + "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/js-cookie": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz", + "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==" + }, + "@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==" + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" + }, + "@types/lodash": { + "version": "4.14.200", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.200.tgz", + "integrity": "sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==" + }, + "@types/mdast": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.12.tgz", + "integrity": "sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==", + "requires": { + "@types/unist": "^2" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "@types/node": { + "version": "20.4.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz", + "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==" + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": 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/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" + }, + "@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/q": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/react": { + "version": "18.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.16.tgz", + "integrity": "sha512-LLFWr12ZhBJ4YVw7neWLe6Pk7Ey5R9OCydfuMsz1L8bZxzaawJj2p06Q8/EFEHDeTBQNFLF62X+CG7B2zIyu0Q==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "17.0.20", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.20.tgz", + "integrity": "sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA==", + "dev": true, + "requires": { + "@types/react": "^17" + }, + "dependencies": { + "@types/react": { + "version": "17.0.62", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.62.tgz", + "integrity": "sha512-eANCyz9DG8p/Vdhr0ZKST8JV12PhH2ACCDYlFw6DIO+D+ca+uP4jtEDEpVqXZrh/uZdXQGwk7whJa3ah5DtyLw==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + } + } + }, + "@types/react-transition-group": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", + "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", + "requires": { + "@types/react": "*" + } + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, + "@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" + }, + "@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "requires": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" + }, + "@types/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "@types/trusted-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" + }, + "@types/unist": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.7.tgz", + "integrity": "sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==" + }, + "@types/ws": { + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.5.tgz", + "integrity": "sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==", + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "devOptional": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "requires": { + "@typescript-eslint/utils": "5.62.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "requires": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "requires": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==" + }, + "@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "requires": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "dependencies": { + "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" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "requires": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==" + } + } + }, + "@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "requires": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@xobotyi/scrollbar-width": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz", + "integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==" + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==" + }, + "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==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==" + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + } + }, + "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-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==" + }, + "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" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "requires": { + "deep-equal": "^2.0.5" + } + }, + "array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "requires": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, + "array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "array.prototype.findlastindex": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", + "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "array.prototype.toreversed": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", + "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + } + }, + "array.prototype.tosorted": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", + "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", + "requires": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.1.0", + "es-shim-unscopables": "^1.0.2" + } + }, + "arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "requires": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==" + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "requires": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "requires": { + "possible-typed-array-names": "^1.0.0" + } + }, + "axe-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", + "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==" + }, + "axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "axios-hooks": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/axios-hooks/-/axios-hooks-3.1.5.tgz", + "integrity": "sha512-mU4WZ9c6YiOTxgTIKbswoHvb/b9fWXa2FxNFKI/hVcfD9Qemz1r9KLfRSVZf1GZg8nFry7oTM5gxNmPSn5PG0Q==", + "requires": { + "@babel/runtime": "7.18.9", + "dequal": "2.0.3", + "lru-cache": "6.0.0" + }, + "dependencies": { + "@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" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "axobject-query": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "requires": { + "dequal": "^2.0.3" + } + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "requires": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-loader": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz", + "integrity": "sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==", + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "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==", + "requires": { + "semver": "^6.0.0" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "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-named-asset-import": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "requires": {} + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", + "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", + "requires": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.2", + "semver": "^6.3.1" + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", + "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.4.2", + "core-js-compat": "^3.31.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", + "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.4.2" + } + }, + "babel-plugin-transform-react-remove-prop-types": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", + "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==" + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@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-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "babel-preset-react-app": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", + "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/plugin-proposal-class-properties": "^7.16.0", + "@babel/plugin-proposal-decorators": "^7.16.4", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", + "@babel/plugin-proposal-numeric-separator": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.16.0", + "@babel/plugin-proposal-private-methods": "^7.16.0", + "@babel/plugin-transform-flow-strip-types": "^7.16.0", + "@babel/plugin-transform-react-display-name": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.4", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.16.0", + "@babel/runtime": "^7.16.3", + "babel-plugin-macros": "^3.1.0", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24" + } + }, + "bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base16": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz", + "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==" + }, + "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==" + }, + "bfj": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", + "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", + "requires": { + "bluebird": "^3.5.5", + "check-types": "^11.1.1", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "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.11.0", + "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==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, + "bootstrap": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", + "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", + "requires": {} + }, + "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==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" + }, + "browserslist": { + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "requires": { + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "requires": { + "node-int64": "^0.4.0" + } + }, + "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==" + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" + }, + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==" + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==" + }, + "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" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==" + }, + "character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" + }, + "check-types": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", + "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==" + }, + "ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==" + }, + "cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" + }, + "classcat": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.4.tgz", + "integrity": "sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==" + }, + "classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "clean-css": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", + "integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", + "requires": { + "source-map": "~0.6.0" + } + }, + "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==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==" + }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==" + }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "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==", + "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==", + "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==" + }, + "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==" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==" + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==" + }, + "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==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, + "core-js": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.31.1.tgz", + "integrity": "sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ==" + }, + "core-js-compat": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", + "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "requires": { + "browserslist": "^4.21.9" + } + }, + "core-js-pure": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.31.1.tgz", + "integrity": "sha512-w+C62kvWti0EPs4KPMCMVv9DriHSXfQOCQ94bGGBiEW5rrbtt/Rz8n5Krhfw9cpFyzXBjf3DB3QnPdEzGDY4Fw==" + }, + "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==" + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "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-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "requires": { + "node-fetch": "^2.6.12" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" + }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-declaration-sorter": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "requires": {} + }, + "css-functions-list": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.0.tgz", + "integrity": "sha512-d/jBMPyYybkkLVypgtGv12R+pIFw4/f/IHtCTxWpZc8ofTYOPigIgmA6vu5rMHartZC+WuXhBUHfnyNUIQSYrg==", + "dev": true + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-in-js-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz", + "integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==", + "requires": { + "hyphenate-style-name": "^1.0.3" + } + }, + "css-loader": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", + "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.21", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.3", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "css-minimizer-webpack-plugin": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz", + "integrity": "sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==", + "requires": { + "cssnano": "^5.0.6", + "jest-worker": "^27.0.2", + "postcss": "^8.3.5", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" + }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, + "cssdb": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz", + "integrity": "sha512-1hN+I3r4VqSNQ+OmMXxYexnumbOONkSil0TWMebVXHtzYW4tRRPovUNHPHj2d4nrgOuYJ8Vs3XwvywsuwwXNNA==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "cssnano": { + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "requires": { + "cssnano-preset-default": "^5.2.14", + "lilconfig": "^2.0.3", + "yaml": "^1.10.2" + } + }, + "cssnano-preset-default": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", + "requires": { + "css-declaration-sorter": "^6.3.1", + "cssnano-utils": "^3.1.0", + "postcss-calc": "^8.2.3", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", + "postcss-discard-comments": "^5.1.2", + "postcss-discard-duplicates": "^5.1.0", + "postcss-discard-empty": "^5.1.1", + "postcss-discard-overridden": "^5.1.0", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", + "postcss-minify-font-values": "^5.1.0", + "postcss-minify-gradients": "^5.1.1", + "postcss-minify-params": "^5.1.4", + "postcss-minify-selectors": "^5.2.1", + "postcss-normalize-charset": "^5.1.0", + "postcss-normalize-display-values": "^5.1.0", + "postcss-normalize-positions": "^5.1.1", + "postcss-normalize-repeat-style": "^5.1.1", + "postcss-normalize-string": "^5.1.0", + "postcss-normalize-timing-functions": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", + "postcss-normalize-url": "^5.1.0", + "postcss-normalize-whitespace": "^5.1.1", + "postcss-ordered-values": "^5.1.3", + "postcss-reduce-initial": "^5.1.2", + "postcss-reduce-transforms": "^5.1.0", + "postcss-svgo": "^5.1.0", + "postcss-unique-selectors": "^5.1.1" + } + }, + "cssnano-utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "requires": {} + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==" + } + } + }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "requires": { + "internmap": "^1.0.0" + } + }, + "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-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "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-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "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-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "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" + } + }, + "damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + } + }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, + "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 + }, + "decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "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 + } + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, + "decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "requires": { + "character-entities": "^2.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==" + }, + "deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, + "deep-equal": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", + "dev": true, + "requires": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.1", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.0", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + } + }, + "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==" + }, + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + }, + "default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "requires": { + "execa": "^5.0.0" + } + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==" + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "detect-port-alt": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", + "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", + "requires": { + "address": "^1.0.1", + "debug": "^2.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==" + } + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + }, + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" + }, + "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==" + }, + "dns-packet": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.0.tgz", + "integrity": "sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ==", + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "requires": { + "utila": "~0.4" + } + }, + "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-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" + }, + "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==" + }, + "ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.4.471", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.471.tgz", + "integrity": "sha512-GpmGRC1vTl60w/k6YpQ18pSiqnmr0j3un//5TV1idPi6aheNfkT1Ye71tMEabWyNDO6sBMgAR+95Eb0eUUr1tA==" + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + }, + "enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "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" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "requires": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + } + }, + "es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + } + }, + "es-module-lexer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==" + }, + "es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "requires": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + } + }, + "es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "requires": { + "hasown": "^2.0.0" + } + }, + "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==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "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==" + }, + "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==" + }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "eslint": { + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.48.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "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.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "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.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "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" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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==", + "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==" + }, + "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==" + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "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==" + }, + "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" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "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==" + } + } + }, + "eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + } + }, + "eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + } + }, + "eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "requires": {} + }, + "eslint-config-react-app": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", + "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", + "requires": { + "@babel/core": "^7.16.0", + "@babel/eslint-parser": "^7.16.3", + "@rushstack/eslint-patch": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^5.5.0", + "@typescript-eslint/parser": "^5.5.0", + "babel-preset-react-app": "^10.0.1", + "confusing-browser-globals": "^1.0.11", + "eslint-plugin-flowtype": "^8.0.3", + "eslint-plugin-import": "^2.25.3", + "eslint-plugin-jest": "^25.3.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-react": "^7.27.1", + "eslint-plugin-react-hooks": "^4.3.0", + "eslint-plugin-testing-library": "^5.0.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "requires": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-flowtype": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", + "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", + "requires": { + "lodash": "^4.17.21", + "string-natural-compare": "^3.0.1" + } + }, + "eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "requires": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-jest": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", + "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", + "requires": { + "@typescript-eslint/experimental-utils": "^5.0.0" + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", + "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "requires": { + "@babel/runtime": "^7.23.2", + "aria-query": "^5.3.0", + "array-includes": "^3.1.7", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "=4.7.0", + "axobject-query": "^3.2.1", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "es-iterator-helpers": "^1.0.15", + "hasown": "^2.0.0", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7" + }, + "dependencies": { + "aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "requires": { + "dequal": "^2.0.3" + } + } + } + }, + "eslint-plugin-react": { + "version": "7.34.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", + "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", + "requires": { + "array-includes": "^3.1.7", + "array.prototype.findlast": "^1.2.4", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.toreversed": "^1.1.2", + "array.prototype.tosorted": "^1.1.3", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.17", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.7", + "object.fromentries": "^2.0.7", + "object.hasown": "^1.1.3", + "object.values": "^1.1.7", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.10" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "requires": { + "esutils": "^2.0.2" + } + }, + "resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "requires": {} + }, + "eslint-plugin-testing-library": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz", + "integrity": "sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==", + "requires": { + "@typescript-eslint/utils": "^5.58.0" + } + }, + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "requires": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "requires": { + "estraverse": "^5.1.0" + } + }, + "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" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + }, + "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==" + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==" + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "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.11.0", + "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==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "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-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==" + }, + "fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "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==", + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "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==" + }, + "fast-loops": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz", + "integrity": "sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==" + }, + "fast-shallow-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz", + "integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==" + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true + }, + "fastest-stable-stringify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz", + "integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==" + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "requires": { + "bser": "2.1.1" + } + }, + "fbemitter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fbemitter/-/fbemitter-3.0.0.tgz", + "integrity": "sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==", + "requires": { + "fbjs": "^3.0.0" + } + }, + "fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "requires": { + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" + } + }, + "fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==" + }, + "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==", + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "filesize": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", + "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==" + }, + "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==", + "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==", + "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==", + "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==" + } + } + }, + "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==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "dependencies": { + "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==", + "requires": { + "semver": "^6.0.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" + } + }, + "flag-icons": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.1.0.tgz", + "integrity": "sha512-AH4v++19bpC5P3Wh767top4wylJYJCWkFnvNiDqGHDxqSqdMZ49jpLXp8PWBHTTXaNQ+/A+QPrOwyiIGaiIhmw==" + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + }, + "flux": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/flux/-/flux-4.0.4.tgz", + "integrity": "sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw==", + "requires": { + "fbemitter": "^3.0.0", + "fbjs": "^3.0.1" + } + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", + "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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==", + "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==" + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "formik": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.5.tgz", + "integrity": "sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.1", + "deepmerge": "^2.1.1", + "hoist-non-react-statics": "^3.3.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "react-fast-compare": "^2.0.1", + "tiny-warning": "^1.0.2", + "tslib": "^2.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-monkey": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==" + }, + "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==", + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + } + }, + "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==" + }, + "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": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==" + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "requires": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + } + }, + "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==", + "requires": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "requires": { + "duplexer": "^0.1.2" + } + }, + "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==" + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==" + }, + "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==" + }, + "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.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + }, + "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.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "hast-util-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", + "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==" + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "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" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "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==", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "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" + } + }, + "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==" + }, + "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" + } + } + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==" + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + } + }, + "html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true + }, + "html-webpack-plugin": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", + "integrity": "sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==", + "requires": { + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + }, + "dependencies": { + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + } + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==" + }, + "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==", + "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==" + }, + "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==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==" + } + } + }, + "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" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, + "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.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} + }, + "idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, + "identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "requires": { + "harmony-reflect": "^1.4.6" + } + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + }, + "immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" + }, + "immutable": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", + "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==", + "devOptional": 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" + }, + "dependencies": { + "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==" + } + } + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "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==" + }, + "inline-style-parser": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", + "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" + }, + "inline-style-prefixer": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.0.tgz", + "integrity": "sha512-I7GEdScunP1dQ6IM2mQWh6v0mOYdYmH3Bp31UecKdrcUgcURTcctSe1IECdUznSHKSmsHtjrT3CwCPI1pyxfUQ==", + "requires": { + "css-in-js-utils": "^3.1.0", + "fast-loops": "^1.1.3" + } + }, + "internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "requires": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + } + }, + "internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==" + }, + "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-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "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==", + "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==", + "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==" + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" + }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "requires": { + "hasown": "^2.0.0" + } + }, + "is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "requires": { + "is-typed-array": "^1.1.13" + } + }, + "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==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "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==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "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-lite": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-1.2.1.tgz", + "integrity": "sha512-pgF+L5bxC+10hLBgf6R2P4ZZUBOQIIacbdo8YvuCP8/JvsWxG7aZ9p10DYuLtifFci4l3VITphhMlMV4Y+urPw==" + }, + "is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==" + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + }, + "is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "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==", + "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==" + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==" + }, + "is-root": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", + "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==" + }, + "is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==" + }, + "is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "requires": { + "call-bind": "^1.0.7" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "requires": { + "which-typed-array": "^1.1.14" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + }, + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "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==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==" + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "requires": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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==", + "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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + } + }, + "jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^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==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "requires": {} + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + } + }, + "jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "devOptional": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "devOptional": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-jasmine2": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", + "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "throat": "^6.0.1" + }, + "dependencies": { + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "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" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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==", + "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==" + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==" + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==" + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + } + } + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^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==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "requires": {} + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "devOptional": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "devOptional": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": 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==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "requires": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + } + }, + "jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-serializer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", + "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", + "requires": { + "@types/node": "*", + "graceful-fs": "^4.2.9" + } + }, + "jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.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==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "devOptional": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.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==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": 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==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "devOptional": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "devOptional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "devOptional": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "devOptional": 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==", + "devOptional": 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==", + "devOptional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "devOptional": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "devOptional": true + } + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "devOptional": 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==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "requires": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": 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==", + "dev": 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==", + "dev": 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==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "devOptional": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "devOptional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==" + }, + "js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" + }, + "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": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "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": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "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==" + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", + "requires": { + "debug": "^2.1.3" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==" + } + } + }, + "jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==" + }, + "jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "requires": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + } + }, + "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==" + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + }, + "klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" + }, + "known-css-properties": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", + "dev": true + }, + "language-subtag-registry": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", + "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" + }, + "language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "requires": { + "language-subtag-registry": "^0.3.20" + } + }, + "launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "requires": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" + }, + "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==" + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" + }, + "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==", + "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==" + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.curry": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", + "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==" + }, + "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.flow": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", + "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==" + }, + "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==" + }, + "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==" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==" + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + }, + "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" + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "requires": { + "semver": "^7.5.3" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "requires": { + "tmpl": "1.0.5" + } + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "requires": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "mdast-util-definitions": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", + "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-from-markdown": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", + "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "requires": { + "@types/mdast": "^3.0.0", + "@types/unist": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "mdast-util-to-string": "^3.1.0", + "micromark": "^3.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-decode-string": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "unist-util-stringify-position": "^3.0.0", + "uvu": "^0.5.0" + } + }, + "mdast-util-to-hast": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", + "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-definitions": "^5.0.0", + "micromark-util-sanitize-uri": "^1.1.0", + "trim-lines": "^3.0.0", + "unist-util-generated": "^2.0.0", + "unist-util-position": "^4.0.0", + "unist-util-visit": "^4.0.0" + } + }, + "mdast-util-to-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", + "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "requires": { + "@types/mdast": "^3.0.0" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "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==" + }, + "memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "requires": { + "fs-monkey": "^1.0.4" + } + }, + "memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" + }, + "micromark": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", + "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", + "requires": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "micromark-core-commonmark": "^1.0.1", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-combine-extensions": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-sanitize-uri": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-core-commonmark": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", + "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-factory-destination": "^1.0.0", + "micromark-factory-label": "^1.0.0", + "micromark-factory-space": "^1.0.0", + "micromark-factory-title": "^1.0.0", + "micromark-factory-whitespace": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-chunked": "^1.0.0", + "micromark-util-classify-character": "^1.0.0", + "micromark-util-html-tag-name": "^1.0.0", + "micromark-util-normalize-identifier": "^1.0.0", + "micromark-util-resolve-all": "^1.0.0", + "micromark-util-subtokenize": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.1", + "uvu": "^0.5.0" + } + }, + "micromark-factory-destination": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", + "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-label": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", + "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-factory-space": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", + "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-title": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", + "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-factory-whitespace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", + "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", + "requires": { + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-character": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", + "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", + "requires": { + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-chunked": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", + "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-classify-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", + "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-combine-extensions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", + "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-decode-numeric-character-reference": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", + "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-decode-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", + "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", + "requires": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-decode-numeric-character-reference": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", + "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==" + }, + "micromark-util-html-tag-name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", + "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==" + }, + "micromark-util-normalize-identifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", + "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", + "requires": { + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-resolve-all": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", + "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", + "requires": { + "micromark-util-types": "^1.0.0" + } + }, + "micromark-util-sanitize-uri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", + "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", + "requires": { + "micromark-util-character": "^1.0.0", + "micromark-util-encode": "^1.0.0", + "micromark-util-symbol": "^1.0.0" + } + }, + "micromark-util-subtokenize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", + "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", + "requires": { + "micromark-util-chunked": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + } + }, + "micromark-util-symbol": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", + "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==" + }, + "micromark-util-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", + "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==" + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "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==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", + "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "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==" + }, + "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.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "requires": { + "minimist": "^1.2.6" + } + }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" + }, + "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": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nano-css": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.1.tgz", + "integrity": "sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "css-tree": "^1.1.2", + "csstype": "^3.1.2", + "fastest-stable-stringify": "^2.0.2", + "inline-style-prefixer": "^7.0.0", + "rtl-css-js": "^1.16.1", + "stacktrace-js": "^2.0.2", + "stylis": "^4.3.0" + }, + "dependencies": { + "stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + } + } + }, + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "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==" + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "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==" + }, + "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" + } + } + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" + }, + "node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "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==" + }, + "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==" + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, + "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-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + }, + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" + }, + "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.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "requires": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", + "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", + "requires": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "safe-array-concat": "^1.0.0" + } + }, + "object.groupby": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", + "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1" + } + }, + "object.hasown": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "requires": { + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + } + }, + "object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "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==", + "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==" + }, + "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" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.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" + }, + "dependencies": { + "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-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + } + }, + "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==" + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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-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" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "requires": { + "entities": "^4.4.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "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": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "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==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" + }, + "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==", + "requires": { + "find-up": "^4.0.0" + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.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==" + } + } + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + }, + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" + }, + "postcss": { + "version": "8.4.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", + "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-browser-comments": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "requires": {} + }, + "postcss-calc": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", + "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "requires": { + "postcss-selector-parser": "^6.0.9", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-colormin": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "colord": "^2.9.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-convert-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-discard-comments": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "requires": {} + }, + "postcss-discard-duplicates": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "requires": {} + }, + "postcss-discard-empty": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "requires": {} + }, + "postcss-discard-overridden": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "requires": {} + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-flexbugs-fixes": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "requires": {} + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "requires": {} + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "dependencies": { + "yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==" + } + } + }, + "postcss-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", + "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.5" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "requires": {} + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "postcss-merge-longhand": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "requires": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^5.1.1" + } + }, + "postcss-merge-rules": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^3.1.0", + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-minify-font-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", + "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-gradients": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", + "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "requires": { + "colord": "^2.9.1", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-params": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "requires": { + "browserslist": "^4.21.4", + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-minify-selectors": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", + "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "requires": { + "postcss-selector-parser": "^6.0.11" + } + }, + "postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-normalize": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz", + "integrity": "sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==", + "requires": { + "@csstools/normalize.css": "*", + "postcss-browser-comments": "^4", + "sanitize.css": "*" + } + }, + "postcss-normalize-charset": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "requires": {} + }, + "postcss-normalize-display-values": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", + "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-positions": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", + "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-repeat-style": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", + "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-string": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", + "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-timing-functions": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", + "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-unicode": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "requires": { + "browserslist": "^4.21.4", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", + "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "requires": { + "normalize-url": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-normalize-whitespace": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", + "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "requires": {} + }, + "postcss-ordered-values": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", + "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "requires": { + "cssnano-utils": "^3.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz", + "integrity": "sha512-T1LgRm5uEVFSEF83vHZJV2z19lHg4yJuZ6gXZZkqVsqv63nlr6zabMH3l4Pc01FQCyfWVrh2GaUeCVy9Po+Aag==", + "requires": { + "@csstools/postcss-cascade-layers": "^1.1.1", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.1.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.10", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.2.0", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-reduce-initial": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "requires": { + "browserslist": "^4.21.4", + "caniuse-api": "^3.0.0" + } + }, + "postcss-reduce-transforms": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", + "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "requires": {} + }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "dev": true + }, + "postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "requires": {} + }, + "postcss-scss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-svgo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", + "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "requires": { + "postcss-value-parser": "^4.2.0", + "svgo": "^2.7.0" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + } + } + } + }, + "postcss-unique-selectors": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", + "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "requires": { + "postcss-selector-parser": "^6.0.5" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" + }, + "prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" + }, + "pretty-error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", + "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", + "requires": { + "lodash": "^4.17.20", + "renderkid": "^3.0.0" + } + }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "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==" + } + } + }, + "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==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "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" + } + }, + "property-information": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.2.0.tgz", + "integrity": "sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==" + }, + "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==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "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==" + } + } + }, + "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==" + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + }, + "pure-color": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz", + "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==" + }, + "pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "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==" + }, + "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==", + "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==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "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-app-polyfill": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", + "integrity": "sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==", + "requires": { + "core-js": "^3.19.2", + "object-assign": "^4.1.1", + "promise": "^8.1.0", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.9", + "whatwg-fetch": "^3.6.2" + }, + "dependencies": { + "promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "requires": { + "asap": "~2.0.6" + } + } + } + }, + "react-async-script": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz", + "integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==", + "requires": { + "hoist-non-react-statics": "^3.3.0", + "prop-types": "^15.5.0" + } + }, + "react-base16-styling": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.6.0.tgz", + "integrity": "sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==", + "requires": { + "base16": "^1.0.0", + "lodash.curry": "^4.0.1", + "lodash.flow": "^3.3.0", + "pure-color": "^1.2.0" + } + }, + "react-compound-slider": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-3.4.0.tgz", + "integrity": "sha512-KSje/rB0xSvvcb7YV0+82hkiXTV5ljSS7axKrNiXLf9AEO+rrr1Xq4MJWA+6v030YNNo/RoSoEB6D6fnoy+8ng==", + "requires": { + "@babel/runtime": "^7.12.5", + "d3-array": "^2.8.0", + "warning": "^4.0.3" + } + }, + "react-dev-utils": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", + "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "address": "^1.1.2", + "browserslist": "^4.18.1", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "detect-port-alt": "^1.1.6", + "escape-string-regexp": "^4.0.0", + "filesize": "^8.0.6", + "find-up": "^5.0.0", + "fork-ts-checker-webpack-plugin": "^6.5.0", + "global-modules": "^2.0.0", + "globby": "^11.0.4", + "gzip-size": "^6.0.0", + "immer": "^9.0.7", + "is-root": "^2.1.0", + "loader-utils": "^3.2.0", + "open": "^8.4.0", + "pkg-up": "^3.1.0", + "prompts": "^2.4.2", + "react-error-overlay": "^6.0.11", + "recursive-readdir": "^2.2.2", + "shell-quote": "^1.7.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "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==", + "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==" + }, + "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==" + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==" + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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-error-boundary": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz", + "integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==", + "requires": { + "@babel/runtime": "^7.12.5" + } + }, + "react-error-overlay": { + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", + "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" + }, + "react-fast-compare": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", + "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" + }, + "react-floater": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/react-floater/-/react-floater-0.7.9.tgz", + "integrity": "sha512-NXqyp9o8FAXOATOEo0ZpyaQ2KPb4cmPMXGWkx377QtJkIXHlHRAGer7ai0r0C1kG5gf+KJ6Gy+gdNIiosvSicg==", + "requires": { + "deepmerge": "^4.3.1", + "is-lite": "^0.8.2", + "popper.js": "^1.16.0", + "prop-types": "^15.8.1", + "tree-changes": "^0.9.1" + }, + "dependencies": { + "@gilbarbara/deep-equal": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA==" + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "is-lite": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/is-lite/-/is-lite-0.8.2.tgz", + "integrity": "sha512-JZfH47qTsslwaAsqbMI3Q6HNNjUuq6Cmzzww50TdP5Esb6e1y2sK2UAaZZuzfAzpoI2AkxoPQapZdlDuP6Vlsw==" + }, + "tree-changes": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.9.3.tgz", + "integrity": "sha512-vvvS+O6kEeGRzMglTKbc19ltLWNtmNt1cpBoSYLj/iEcPVvpJasemKOlxBrmZaCtDJoF+4bwv3m01UKYi8mukQ==", + "requires": { + "@gilbarbara/deep-equal": "^0.1.1", + "is-lite": "^0.8.2" + } + } + } + }, + "react-google-recaptcha": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-2.1.0.tgz", + "integrity": "sha512-K9jr7e0CWFigi8KxC3WPvNqZZ47df2RrMAta6KmRoE4RUi7Ys6NmNjytpXpg4HI/svmQJLKR+PncEPaNJ98DqQ==", + "requires": { + "prop-types": "^15.5.0", + "react-async-script": "^1.1.1" + } + }, + "react-icons": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz", + "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==", + "requires": {} + }, + "react-infinite-scroll-component": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-infinite-scroll-component/-/react-infinite-scroll-component-6.1.0.tgz", + "integrity": "sha512-SQu5nCqy8DxQWpnUVLx7V7b7LcA37aM7tvoWjTLZp1dk6EJibM5/4EJKzOnl07/BsM1Y40sKLuqjCwwH/xV0TQ==", + "requires": { + "throttle-debounce": "^2.1.0" + } + }, + "react-innertext": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-innertext/-/react-innertext-1.1.5.tgz", + "integrity": "sha512-PWAqdqhxhHIv80dT9znP2KvS+hfkbRovFp4zFYHFFlOoQLRiawIic81gKb3U1wEyJZgMwgs3JoLtwryASRWP3Q==", + "requires": {} + }, + "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==" + }, + "react-joyride": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/react-joyride/-/react-joyride-2.8.1.tgz", + "integrity": "sha512-fVwCmoOvJsiFKKHn8mvPUYc4JUUkgAsQMvarpZDtFPTc4duj240b12+AB8+3NXlTYGZVnKNSTgFFzoSh9RxjmQ==", + "requires": { + "@gilbarbara/deep-equal": "^0.3.1", + "deep-diff": "^1.0.2", + "deepmerge": "^4.3.1", + "is-lite": "^1.2.1", + "react-floater": "^0.7.9", + "react-innertext": "^1.1.5", + "react-is": "^16.13.1", + "scroll": "^3.0.1", + "scrollparent": "^2.1.0", + "tree-changes": "^0.11.2", + "type-fest": "^4.15.0" + }, + "dependencies": { + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "type-fest": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.18.2.tgz", + "integrity": "sha512-+suCYpfJLAe4OXS6+PPXjW3urOS4IoP9waSiLuXfLgqZODKw/aWwASvzqE886wA0kQgGy0mIWyhd87VpqIy6Xg==" + } + } + }, + "react-json-editor-ajrm": { + "version": "2.5.14", + "resolved": "https://registry.npmjs.org/react-json-editor-ajrm/-/react-json-editor-ajrm-2.5.14.tgz", + "integrity": "sha512-z2035l5lnU0Wo73AUonj68d1Eh9vjVcLXEa8YogDEeFPcogzYkfT6R38fvYlLKDhP0oI+LyOO/IBWsFkXs4M0Q==", + "requires": { + "@babel/runtime": "^7.0.0-rc.0" + } + }, + "react-json-tree": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.18.0.tgz", + "integrity": "sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==", + "requires": { + "@babel/runtime": "^7.20.6", + "@types/lodash": "^4.14.191", + "react-base16-styling": "^0.9.1" + }, + "dependencies": { + "react-base16-styling": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz", + "integrity": "sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==", + "requires": { + "@babel/runtime": "^7.16.7", + "@types/base16": "^1.0.2", + "@types/lodash": "^4.14.178", + "base16": "^1.0.0", + "color": "^3.2.1", + "csstype": "^3.0.10", + "lodash.curry": "^4.1.1" + } + } + } + }, + "react-json-view": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/react-json-view/-/react-json-view-1.21.3.tgz", + "integrity": "sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==", + "requires": { + "flux": "^4.0.1", + "react-base16-styling": "^0.6.0", + "react-lifecycles-compat": "^3.0.4", + "react-textarea-autosize": "^8.3.2" + } + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-markdown": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-8.0.7.tgz", + "integrity": "sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==", + "requires": { + "@types/hast": "^2.0.0", + "@types/prop-types": "^15.0.0", + "@types/unist": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^2.0.0", + "prop-types": "^15.0.0", + "property-information": "^6.0.0", + "react-is": "^18.0.0", + "remark-parse": "^10.0.0", + "remark-rehype": "^10.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^0.4.0", + "unified": "^10.0.0", + "unist-util-visit": "^4.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "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-popper": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", + "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", + "requires": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + }, + "dependencies": { + "react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + } + } + }, + "react-refresh": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", + "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==" + }, + "react-router": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", + "integrity": "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==", + "requires": { + "@remix-run/router": "1.15.0" + } + }, + "react-router-dom": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", + "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", + "requires": { + "@remix-run/router": "1.15.0", + "react-router": "6.22.0" + } + }, + "react-scripts": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", + "integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==", + "requires": { + "@babel/core": "^7.16.0", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3", + "@svgr/webpack": "^5.5.0", + "babel-jest": "^27.4.2", + "babel-loader": "^8.2.3", + "babel-plugin-named-asset-import": "^0.3.8", + "babel-preset-react-app": "^10.0.1", + "bfj": "^7.0.2", + "browserslist": "^4.18.1", + "camelcase": "^6.2.1", + "case-sensitive-paths-webpack-plugin": "^2.4.0", + "css-loader": "^6.5.1", + "css-minimizer-webpack-plugin": "^3.2.0", + "dotenv": "^10.0.0", + "dotenv-expand": "^5.1.0", + "eslint": "^8.3.0", + "eslint-config-react-app": "^7.0.1", + "eslint-webpack-plugin": "^3.1.1", + "file-loader": "^6.2.0", + "fs-extra": "^10.0.0", + "fsevents": "^2.3.2", + "html-webpack-plugin": "^5.5.0", + "identity-obj-proxy": "^3.0.0", + "jest": "^27.4.3", + "jest-resolve": "^27.4.2", + "jest-watch-typeahead": "^1.0.0", + "mini-css-extract-plugin": "^2.4.5", + "postcss": "^8.4.4", + "postcss-flexbugs-fixes": "^5.0.2", + "postcss-loader": "^6.2.1", + "postcss-normalize": "^10.0.1", + "postcss-preset-env": "^7.0.1", + "prompts": "^2.4.2", + "react-app-polyfill": "^3.0.0", + "react-dev-utils": "^12.0.1", + "react-refresh": "^0.11.0", + "resolve": "^1.20.0", + "resolve-url-loader": "^4.0.0", + "sass-loader": "^12.3.0", + "semver": "^7.3.5", + "source-map-loader": "^3.0.0", + "style-loader": "^3.3.1", + "tailwindcss": "^3.0.2", + "terser-webpack-plugin": "^5.2.5", + "webpack": "^5.64.4", + "webpack-dev-server": "^4.6.0", + "webpack-manifest-plugin": "^4.0.2", + "workbox-webpack-plugin": "^6.4.1" + }, + "dependencies": { + "@jest/console": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", + "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", + "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/reporters": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^27.5.1", + "jest-config": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-resolve-dependencies": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "jest-watcher": "^27.5.1", + "micromatch": "^4.0.4", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", + "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "requires": { + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1" + } + }, + "@jest/fake-timers": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", + "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "requires": { + "@jest/types": "^27.5.1", + "@sinonjs/fake-timers": "^8.0.1", + "@types/node": "*", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "@jest/globals": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", + "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/types": "^27.5.1", + "expect": "^27.5.1" + } + }, + "@jest/reporters": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", + "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-haste-map": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^8.1.0" + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", + "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", + "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", + "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "requires": { + "@jest/test-result": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-runtime": "^27.5.1" + } + }, + "@jest/transform": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", + "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^27.5.1", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-util": "^27.5.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + } + }, + "@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + } + }, + "@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + }, + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", + "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" + }, + "@types/yargs": { + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==" + } + } + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==" + }, + "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" + } + }, + "babel-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", + "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "requires": { + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", + "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", + "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "requires": { + "babel-plugin-jest-hoist": "^27.5.1", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "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==" + }, + "cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==" + }, + "data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "requires": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + } + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + }, + "diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==" + }, + "domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "requires": { + "webidl-conversions": "^5.0.0" + }, + "dependencies": { + "webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==" + } + } + }, + "emittery": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", + "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==" + }, + "expect": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", + "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "requires": { + "@jest/types": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1" + } + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "requires": { + "whatwg-encoding": "^1.0.5" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "requires": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + } + }, + "jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "requires": { + "@jest/types": "^27.5.1", + "execa": "^5.0.0", + "throat": "^6.0.1" + } + }, + "jest-circus": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", + "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "expect": "^27.5.1", + "is-generator-fn": "^2.0.0", + "jest-each": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3", + "throat": "^6.0.1" + } + }, + "jest-cli": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", + "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "requires": { + "@jest/core": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "prompts": "^2.0.1", + "yargs": "^16.2.0" + } + }, + "jest-config": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", + "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "requires": { + "@babel/core": "^7.8.0", + "@jest/test-sequencer": "^27.5.1", + "@jest/types": "^27.5.1", + "babel-jest": "^27.5.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.9", + "jest-circus": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-jasmine2": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runner": "^27.5.1", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-docblock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", + "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", + "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "jest-util": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-environment-jsdom": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", + "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1", + "jsdom": "^16.6.0" + } + }, + "jest-environment-node": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", + "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "jest-mock": "^27.5.1", + "jest-util": "^27.5.1" + } + }, + "jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==" + }, + "jest-haste-map": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", + "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "requires": { + "@jest/types": "^27.5.1", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^27.5.1", + "jest-serializer": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.4", + "walker": "^1.0.7" + } + }, + "jest-leak-detector": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", + "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "requires": { + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + } + }, + "jest-message-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", + "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^27.5.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^27.5.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*" + } + }, + "jest-regex-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", + "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==" + }, + "jest-resolve": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", + "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "requires": { + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^27.5.1", + "jest-validate": "^27.5.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", + "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "requires": { + "@jest/types": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-snapshot": "^27.5.1" + } + }, + "jest-runner": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", + "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "requires": { + "@jest/console": "^27.5.1", + "@jest/environment": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.8.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^27.5.1", + "jest-environment-jsdom": "^27.5.1", + "jest-environment-node": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-leak-detector": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-runtime": "^27.5.1", + "jest-util": "^27.5.1", + "jest-worker": "^27.5.1", + "source-map-support": "^0.5.6", + "throat": "^6.0.1" + } + }, + "jest-runtime": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", + "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "requires": { + "@jest/environment": "^27.5.1", + "@jest/fake-timers": "^27.5.1", + "@jest/globals": "^27.5.1", + "@jest/source-map": "^27.5.1", + "@jest/test-result": "^27.5.1", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "execa": "^5.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-mock": "^27.5.1", + "jest-regex-util": "^27.5.1", + "jest-resolve": "^27.5.1", + "jest-snapshot": "^27.5.1", + "jest-util": "^27.5.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", + "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "requires": { + "@babel/core": "^7.7.2", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.0.0", + "@jest/transform": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^27.5.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "jest-haste-map": "^27.5.1", + "jest-matcher-utils": "^27.5.1", + "jest-message-util": "^27.5.1", + "jest-util": "^27.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^27.5.1", + "semver": "^7.3.2" + } + }, + "jest-util": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", + "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "requires": { + "@jest/types": "^27.5.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", + "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "requires": { + "@jest/types": "^27.5.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^27.5.1", + "leven": "^3.1.0", + "pretty-format": "^27.5.1" + } + }, + "jest-watch-typeahead": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", + "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", + "requires": { + "ansi-escapes": "^4.3.1", + "chalk": "^4.0.0", + "jest-regex-util": "^28.0.0", + "jest-watcher": "^28.0.0", + "slash": "^4.0.0", + "string-length": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==" + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + } + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==" + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "dependencies": { + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "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" + } + } + } + }, + "pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + }, + "string-length": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", + "requires": { + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" + }, + "dependencies": { + "char-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", + "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==" + } + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + } + } + } + } + }, + "jest-watcher": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", + "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "requires": { + "@jest/test-result": "^27.5.1", + "@jest/types": "^27.5.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^27.5.1", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "requires": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, + "resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==" + }, + "saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "requires": { + "punycode": "^2.1.1" + } + }, + "v8-to-istanbul": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", + "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==" + } + } + }, + "w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "requires": { + "xml-name-validator": "^3.0.0" + } + }, + "webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + }, + "whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "requires": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "requires": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + } + }, + "react-share": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.1.tgz", + "integrity": "sha512-AJ9m9RiJssqvYg7MoJUc9J0D7b/liWrsfQ99ndKc5vJ4oVHHd4Fy87jBlKEQPibT40oYA3AQ/a9/oQY6/yaigw==", + "requires": { + "classnames": "^2.3.2", + "jsonp": "^0.2.1" + } + }, + "react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "requires": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + } + }, + "react-table": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", + "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", + "requires": {} + }, + "react-textarea-autosize": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.2.tgz", + "integrity": "sha512-uOkyjkEl0ByEK21eCJMHDGBAAd/BoFQBawYK5XItjAmCTeSbjxghd8qnt7nzsLYzidjnoObu6M26xts0YGKsGg==", + "requires": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + } + }, + "react-top-loading-bar": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/react-top-loading-bar/-/react-top-loading-bar-2.3.1.tgz", + "integrity": "sha512-rQk2Nm+TOBrM1C4E3e6KwT65iXyRSgBHjCkr2FNja1S51WaPulRA5nKj/xazuQ3x89wDDdGsrqkqy0RBIfd0xg==", + "requires": {} + }, + "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" + } + }, + "react-universal-interface": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz", + "integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==", + "requires": {} + }, + "react-use": { + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/react-use/-/react-use-17.5.0.tgz", + "integrity": "sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==", + "requires": { + "@types/js-cookie": "^2.2.6", + "@xobotyi/scrollbar-width": "^1.9.5", + "copy-to-clipboard": "^3.3.1", + "fast-deep-equal": "^3.1.3", + "fast-shallow-equal": "^1.0.0", + "js-cookie": "^2.2.1", + "nano-css": "^5.6.1", + "react-universal-interface": "^0.6.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.1.0", + "set-harmonic-interval": "^1.0.1", + "throttle-debounce": "^3.0.1", + "ts-easing": "^0.2.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==" + }, + "throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==" + } + } + }, + "reactflow": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.10.4.tgz", + "integrity": "sha512-0CApYhtYicXEDg/x2kvUHiUk26Qur8lAtTtiSlptNKuyEuGti6P1y5cS32YGaUoDMoCqkm/m+jcKkfMOvSCVRA==", + "requires": { + "@reactflow/background": "11.3.9", + "@reactflow/controls": "11.2.9", + "@reactflow/core": "11.10.4", + "@reactflow/minimap": "11.7.9", + "@reactflow/node-resizer": "2.2.9", + "@reactflow/node-toolbar": "1.3.9" + } + }, + "reactstrap": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.2.2.tgz", + "integrity": "sha512-4KroiGOdqZLAnMGzHjpErW3G7bLB+QbKzzMLIDXydPIV0y74lpdL7WtXHkLWAGInd97WCPNx4+R0NQDPyzIfhw==", + "requires": { + "@babel/runtime": "^7.12.5", + "@popperjs/core": "^2.6.0", + "classnames": "^2.2.3", + "prop-types": "^15.5.8", + "react-popper": "^2.2.4", + "react-transition-group": "^4.4.2" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "requires": { + "pify": "^2.3.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "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 + }, + "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" + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "recharts": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.6.tgz", + "integrity": "sha512-D+7j9WI+D0NHauah3fKHuNNcRK8bOypPW7os1DERinogGBGaHI7i6tQKJ0aUF3JXyBZ63dyfKIW2WTOPJDxJ8w==", + "requires": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^16.10.2", + "react-smooth": "^4.0.0", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, + "recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "requires": { + "minimatch": "^3.0.5" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==" + }, + "regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "requires": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + } + }, + "regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "requires": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "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==" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==" + }, + "remark-parse": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", + "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-from-markdown": "^1.0.0", + "unified": "^10.0.0" + } + }, + "remark-rehype": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", + "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", + "requires": { + "@types/hast": "^2.0.0", + "@types/mdast": "^3.0.0", + "mdast-util-to-hast": "^12.1.0", + "unified": "^10.0.0" + } + }, + "remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, + "renderkid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", + "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^6.0.1" + } + }, + "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": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "resolve-url-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz", + "integrity": "sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==", + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^7.0.35", + "source-map": "0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==" + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + } + } + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "devOptional": true + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, + "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" + } + }, + "rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "rtl-css-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", + "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "requires": { + "mri": "^1.1.0" + } + }, + "safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "requires": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + } + }, + "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==" + }, + "safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "requires": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sanitize.css": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", + "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" + }, + "sass": { + "version": "1.77.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.0.tgz", + "integrity": "sha512-eGj4HNfXqBWtSnvItNkn7B6icqH14i3CiCGbzMKs3BAPTq62pp9NBYsBgyN4cA+qssqo9r26lW4JSvlaUUWbgw==", + "devOptional": 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": "12.6.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", + "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "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": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==" + }, + "scroll": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scroll/-/scroll-3.0.1.tgz", + "integrity": "sha512-pz7y517OVls1maEzlirKO5nPYle9AXsFzTMNJrRGmT951mzpIBy7sNHOg5o/0MQd/NqliCiWnAi0kZneMPFLcg==" + }, + "scrollparent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.1.0.tgz", + "integrity": "sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA==" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==" + }, + "selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "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==", + "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==" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "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==", + "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==" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "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==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==" + } + } + }, + "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==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + } + }, + "set-harmonic-interval": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz", + "integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==" + }, + "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==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "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==", + "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==" + }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" + }, + "side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + } + }, + "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==" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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==", + "dev": 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==", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "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==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "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-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz", + "integrity": "sha512-BokxPoLjyl3iOrgkWaakaxqnelAJSS+0V+De0kKIq6lyWrXuiPgYTGp6z3iHmqljKAaLXwZa+ctD8GccRJeVvg==", + "requires": { + "abab": "^2.0.5", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.1" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "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.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "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==", + "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" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "requires": { + "stackframe": "^1.3.4" + } + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" + }, + "stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "requires": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==" + } + } + }, + "stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "requires": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "requires": { + "internal-slot": "^1.0.4" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-natural-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", + "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==" + }, + "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" + }, + "dependencies": { + "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==" + } + } + }, + "string.prototype.matchall": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + } + }, + "string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "requires": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + }, + "strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "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==" + }, + "style-loader": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", + "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", + "requires": {} + }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true + }, + "style-to-object": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.1.tgz", + "integrity": "sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==", + "requires": { + "inline-style-parser": "0.1.1" + } + }, + "stylehacks": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "requires": { + "browserslist": "^4.21.4", + "postcss-selector-parser": "^6.0.4" + } + }, + "stylelint": { + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", + "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^7.1.0", + "css-functions-list": "^3.1.0", + "debug": "^4.3.4", + "fast-glob": "^3.2.12", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^6.0.1", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.1", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.26.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.19", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.3.0", + "svg-tags": "^1.0.0", + "table": "^6.8.1", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.2" + }, + "dependencies": { + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + } + } + }, + "stylelint-config-prettier": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz", + "integrity": "sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==", + "dev": true, + "requires": {} + }, + "stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "requires": {} + }, + "stylelint-config-recommended-scss": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-6.0.0.tgz", + "integrity": "sha512-6QOe2/OzXV2AP5FE12A7+qtKdZik7Saf42SMMl84ksVBBPpTdrV+9HaCbPYiRMiwELY9hXCVdH4wlJ+YJb5eig==", + "dev": true, + "requires": { + "postcss-scss": "^4.0.2", + "stylelint-config-recommended": "^7.0.0", + "stylelint-scss": "^4.0.0" + } + }, + "stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^7.0.0" + } + }, + "stylelint-config-standard-scss": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-scss/-/stylelint-config-standard-scss-4.0.0.tgz", + "integrity": "sha512-xizu8PTEyB6zYXBiVg6VtvUYn9m57x+6ZtaOdaxsfpbe5eagLPGNlbYnKfm/CfN69ArUpnwR6LjgsTHzlGbtXQ==", + "dev": true, + "requires": { + "stylelint-config-recommended-scss": "^6.0.0", + "stylelint-config-standard": "^25.0.0" + } + }, + "stylelint-scss": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-4.7.0.tgz", + "integrity": "sha512-TSUgIeS0H3jqDZnby1UO1Qv3poi1N8wUYIJY6D1tuUq2MN3lwp/rITVo0wD+1SWTmRm0tNmGO0b7nKInnqF6Hg==", + "dev": true, + "requires": { + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0" + } + }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, + "sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "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-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==", + "requires": { + "has-flag": "^4.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==" + }, + "svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + }, + "dependencies": { + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + } + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + } + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "tailwindcss": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==" + }, + "temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==" + }, + "tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "requires": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==" + } + } + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "terser": { + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", + "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "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==" + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "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==" + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" + }, + "throttle-debounce": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-2.3.0.tgz", + "integrity": "sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==" + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + }, + "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==" + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" + }, + "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-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==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "tree-changes": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/tree-changes/-/tree-changes-0.11.2.tgz", + "integrity": "sha512-4gXlUthrl+RabZw6lLvcCDl6KfJOCmrC16BC5CRdut1EAH509Omgg0BfKLY+ViRlzrvYOTWR0FMS2SQTwzumrw==", + "requires": { + "@gilbarbara/deep-equal": "^0.3.1", + "is-lite": "^1.2.0" + } + }, + "trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" + }, + "ts-easing": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", + "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + }, + "tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "requires": { + "minimist": "^1.2.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==" + } + } + }, + "tslib": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + } + }, + "typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "requires": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true + }, + "ua-parser-js": { + "version": "1.0.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.35.tgz", + "integrity": "sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==" + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + }, + "unified": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", + "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "requires": { + "@types/unist": "^2.0.0", + "bail": "^2.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^5.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==" + } + } + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "unist-util-generated": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", + "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==" + }, + "unist-util-is": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", + "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-position": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", + "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", + "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "requires": { + "@types/unist": "^2.0.0" + } + }, + "unist-util-visit": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", + "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0", + "unist-util-visit-parents": "^5.1.1" + } + }, + "unist-util-visit-parents": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", + "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==" + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.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" + } + }, + "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==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "requires": {} + }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} + }, + "use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "requires": { + "use-isomorphic-layout-effect": "^1.1.1" + } + }, + "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-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", + "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" + }, + "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==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "uvu": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", + "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "requires": { + "dequal": "^2.0.0", + "diff": "^5.0.0", + "kleur": "^4.0.3", + "sade": "^1.7.3" + }, + "dependencies": { + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + } + } + }, + "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==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "dependencies": { + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": 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==" + }, + "vfile": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", + "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^3.0.0", + "vfile-message": "^3.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + } + } + }, + "vfile-message": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", + "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^3.0.0" + } + }, + "victory-vendor": { + "version": "36.6.11", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.6.11.tgz", + "integrity": "sha512-nT8kCiJp8dQh8g991J/R5w5eE2KnO8EAIP0xocWlh9l2okngMWglOPoMZzJvek8Q1KUc4XE/mJxTZnvOB1sTYg==", + "requires": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + }, + "dependencies": { + "d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "requires": { + "internmap": "1 - 2" + } + } + } + }, + "w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "requires": { + "browser-process-hrtime": "^1.0.0" + } + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "requires": { + "makeerror": "1.0.12" + } + }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "webpack": { + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "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" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", + "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "webpack-manifest-plugin": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz", + "integrity": "sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==", + "requires": { + "tapable": "^2.0.0", + "webpack-sources": "^2.2.0" + }, + "dependencies": { + "webpack-sources": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz", + "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==", + "requires": { + "source-list-map": "^2.0.1", + "source-map": "^0.6.1" + } + } + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==" + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "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==" + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-fetch": { + "version": "3.6.17", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", + "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==" + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "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==", + "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-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "requires": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "requires": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + } + }, + "workbox-background-sync": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-6.6.0.tgz", + "integrity": "sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==", + "requires": { + "idb": "^7.0.1", + "workbox-core": "6.6.0" + } + }, + "workbox-broadcast-update": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-6.6.0.tgz", + "integrity": "sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==", + "requires": { + "workbox-core": "6.6.0" + } + }, + "workbox-build": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-6.6.0.tgz", + "integrity": "sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==", + "requires": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "6.6.0", + "workbox-broadcast-update": "6.6.0", + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-google-analytics": "6.6.0", + "workbox-navigation-preload": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-range-requests": "6.6.0", + "workbox-recipes": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0", + "workbox-streams": "6.6.0", + "workbox-sw": "6.6.0", + "workbox-window": "6.6.0" + }, + "dependencies": { + "@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "requires": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + } + }, + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "requires": { + "whatwg-url": "^7.0.0" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "requires": { + "punycode": "^2.1.0" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "workbox-cacheable-response": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-6.6.0.tgz", + "integrity": "sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==", + "requires": { + "workbox-core": "6.6.0" + } + }, + "workbox-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.6.0.tgz", + "integrity": "sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==" + }, + "workbox-expiration": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-6.6.0.tgz", + "integrity": "sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==", + "requires": { + "idb": "^7.0.1", + "workbox-core": "6.6.0" + } + }, + "workbox-google-analytics": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-6.6.0.tgz", + "integrity": "sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==", + "requires": { + "workbox-background-sync": "6.6.0", + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "workbox-navigation-preload": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-6.6.0.tgz", + "integrity": "sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==", + "requires": { + "workbox-core": "6.6.0" + } + }, + "workbox-precaching": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-6.6.0.tgz", + "integrity": "sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==", + "requires": { + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "workbox-range-requests": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-6.6.0.tgz", + "integrity": "sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==", + "requires": { + "workbox-core": "6.6.0" + } + }, + "workbox-recipes": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-6.6.0.tgz", + "integrity": "sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==", + "requires": { + "workbox-cacheable-response": "6.6.0", + "workbox-core": "6.6.0", + "workbox-expiration": "6.6.0", + "workbox-precaching": "6.6.0", + "workbox-routing": "6.6.0", + "workbox-strategies": "6.6.0" + } + }, + "workbox-routing": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.6.0.tgz", + "integrity": "sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==", + "requires": { + "workbox-core": "6.6.0" + } + }, + "workbox-strategies": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.6.0.tgz", + "integrity": "sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==", + "requires": { + "workbox-core": "6.6.0" + } + }, + "workbox-streams": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-6.6.0.tgz", + "integrity": "sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==", + "requires": { + "workbox-core": "6.6.0", + "workbox-routing": "6.6.0" + } + }, + "workbox-sw": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-6.6.0.tgz", + "integrity": "sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==" + }, + "workbox-webpack-plugin": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.0.tgz", + "integrity": "sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==", + "requires": { + "fast-json-stable-stringify": "^2.1.0", + "pretty-bytes": "^5.4.1", + "upath": "^1.2.0", + "webpack-sources": "^1.4.3", + "workbox-build": "6.6.0" + }, + "dependencies": { + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "workbox-window": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-6.6.0.tgz", + "integrity": "sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==", + "requires": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "6.6.0" + } + }, + "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" + }, + "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" + } + }, + "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==" + } + } + }, + "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": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "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" + }, + "dependencies": { + "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==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + }, + "zustand": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.2.tgz", + "integrity": "sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==", + "requires": { + "use-sync-external-store": "1.2.0" + } + } + } +} diff --git a/Submodules/IntelOwl/frontend/package.json b/Submodules/IntelOwl/frontend/package.json new file mode 100644 index 0000000..f48965f --- /dev/null +++ b/Submodules/IntelOwl/frontend/package.json @@ -0,0 +1,85 @@ +{ + "name": "intelowl", + "version": "6.0.0", + "private": true, + "proxy": "http://localhost:80/", + "dependencies": { + "@certego/certego-ui": "^0.1.13", + "@dagrejs/dagre": "^1.0.4", + "axios": "^1.6.0", + "axios-hooks": "^3.1.5", + "bootstrap": "^5.3.2", + "classnames": "^2.5.1", + "flag-icons": "^7.1.0", + "formik": "^2.4.5", + "js-cookie": "^3.0.5", + "md5": "^2.3.0", + "prop-types": "^15.8.1", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-error-boundary": "^4.0.11", + "react-google-recaptcha": "^2.1.0", + "react-icons": "^4.12.0", + "react-joyride": "^2.8.1", + "react-json-tree": "^0.18.0", + "react-markdown": "^8.0.7", + "react-router-dom": "^6.22.0", + "react-scripts": "^5.0.1", + "react-select": "^5.8.0", + "react-table": "^7.8.0", + "react-use": "^17.5.0", + "reactflow": "^11.10.4", + "reactstrap": "^9.2.1", + "recharts": "^2.12.6", + "zustand": "^4.5.2" + }, + "scripts": { + "start": "PORT=3001 react-scripts start", + "build": "react-scripts build", + "test": "jest", + "eject": "react-scripts eject", + "lint": "eslint src/ --ext .js,.jsx", + "lint:fix": "npm run lint -- --fix", + "lint-sass": "stylelint \"src/styles/*.{css,scss}\"", + "lint-sass:fix": "npm run lint-sass -- --fix", + "prettier:write": "prettier --write .", + "prettier:check": "prettier --check .", + "prettier:eslint-config-prettier": "eslint-config-prettier index.jsx", + "prettier:stylelint-check": "stylelint-config-prettier-check" + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@babel/preset-env": "^7.21.4", + "@babel/preset-react": "^7.23.3", + "@testing-library/jest-dom": "^6.4.2", + "@testing-library/react": "^12.1.5", + "@testing-library/user-event": "^14.5.2", + "babel-eslint": "^10.1.0", + "babel-jest": "^29.7.0", + "eslint": "^8.48.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.5.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "prettier": "^3.2.5", + "sass": "^1.77.0", + "stylelint": "^14.9.1", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-standard-scss": "^4.0.0" + } +} diff --git a/Submodules/IntelOwl/frontend/public/env_template.js b/Submodules/IntelOwl/frontend/public/env_template.js new file mode 100644 index 0000000..e2bf7a2 --- /dev/null +++ b/Submodules/IntelOwl/frontend/public/env_template.js @@ -0,0 +1,7 @@ +window.$env = { + // Choose one of the below, + // a) localhost, internal deployments + RECAPTCHA_SITEKEY: "", + // b) public deployment + // RECAPTCHA_SITEKEY: "", +}; diff --git a/Submodules/IntelOwl/frontend/public/google-logo.svg b/Submodules/IntelOwl/frontend/public/google-logo.svg new file mode 100644 index 0000000..ee593d7 --- /dev/null +++ b/Submodules/IntelOwl/frontend/public/google-logo.svg @@ -0,0 +1,43 @@ + + + + btn_google_light_pressed_ios + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Submodules/IntelOwl/frontend/public/icons/192x192.png b/Submodules/IntelOwl/frontend/public/icons/192x192.png new file mode 100644 index 0000000..943a05b Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/192x192.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/512x512.png b/Submodules/IntelOwl/frontend/public/icons/512x512.png new file mode 100644 index 0000000..8e749ad Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/512x512.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/apple-touch-icon.png b/Submodules/IntelOwl/frontend/public/icons/apple-touch-icon.png new file mode 100644 index 0000000..facfdc8 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/apple-touch-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/browserconfig.xml b/Submodules/IntelOwl/frontend/public/icons/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/Submodules/IntelOwl/frontend/public/icons/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/Submodules/IntelOwl/frontend/public/icons/cloudflare-icon.png b/Submodules/IntelOwl/frontend/public/icons/cloudflare-icon.png new file mode 100644 index 0000000..d60b8d9 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/cloudflare-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/favicon-16x16.png b/Submodules/IntelOwl/frontend/public/icons/favicon-16x16.png new file mode 100644 index 0000000..8b3e107 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/favicon-16x16.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/favicon-32x32.png b/Submodules/IntelOwl/frontend/public/icons/favicon-32x32.png new file mode 100644 index 0000000..8e730f5 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/favicon-32x32.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/favicon-notification.ico b/Submodules/IntelOwl/frontend/public/icons/favicon-notification.ico new file mode 100644 index 0000000..c4c97db Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/favicon-notification.ico differ diff --git a/Submodules/IntelOwl/frontend/public/icons/favicon.ico b/Submodules/IntelOwl/frontend/public/icons/favicon.ico new file mode 100644 index 0000000..b8c34d5 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/favicon.ico differ diff --git a/Submodules/IntelOwl/frontend/public/icons/google-icon.png b/Submodules/IntelOwl/frontend/public/icons/google-icon.png new file mode 100644 index 0000000..b600c0a Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/google-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/honeynet.ico b/Submodules/IntelOwl/frontend/public/icons/honeynet.ico new file mode 100644 index 0000000..a5b3967 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/honeynet.ico differ diff --git a/Submodules/IntelOwl/frontend/public/icons/hybrid-analysis-icon.png b/Submodules/IntelOwl/frontend/public/icons/hybrid-analysis-icon.png new file mode 100644 index 0000000..4c2c73d Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/hybrid-analysis-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/mstile-150x150.png b/Submodules/IntelOwl/frontend/public/icons/mstile-150x150.png new file mode 100644 index 0000000..03c9a8f Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/mstile-150x150.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/quad9-icon.png b/Submodules/IntelOwl/frontend/public/icons/quad9-icon.png new file mode 100644 index 0000000..6a35a71 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/quad9-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/quokka-icon.png b/Submodules/IntelOwl/frontend/public/icons/quokka-icon.png new file mode 100644 index 0000000..79747fe Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/quokka-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/icons/urlhaus-icon.png b/Submodules/IntelOwl/frontend/public/icons/urlhaus-icon.png new file mode 100644 index 0000000..165d983 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/icons/urlhaus-icon.png differ diff --git a/Submodules/IntelOwl/frontend/public/index.html b/Submodules/IntelOwl/frontend/public/index.html new file mode 100644 index 0000000..4476dec --- /dev/null +++ b/Submodules/IntelOwl/frontend/public/index.html @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + IntelOwl + + + +
+ + + diff --git a/Submodules/IntelOwl/frontend/public/logo-blue.png b/Submodules/IntelOwl/frontend/public/logo-blue.png new file mode 100644 index 0000000..e873829 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/logo-blue.png differ diff --git a/Submodules/IntelOwl/frontend/public/logo-dark.png b/Submodules/IntelOwl/frontend/public/logo-dark.png new file mode 100644 index 0000000..bda9751 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/logo-dark.png differ diff --git a/Submodules/IntelOwl/frontend/public/logo-negative-reduced.png b/Submodules/IntelOwl/frontend/public/logo-negative-reduced.png new file mode 100644 index 0000000..9e5b8a1 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/logo-negative-reduced.png differ diff --git a/Submodules/IntelOwl/frontend/public/logo-negative.png b/Submodules/IntelOwl/frontend/public/logo-negative.png new file mode 100644 index 0000000..4ae036c Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/logo-negative.png differ diff --git a/Submodules/IntelOwl/frontend/public/logo-positive.png b/Submodules/IntelOwl/frontend/public/logo-positive.png new file mode 100644 index 0000000..58fba9d Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/logo-positive.png differ diff --git a/Submodules/IntelOwl/frontend/public/logo-white.png b/Submodules/IntelOwl/frontend/public/logo-white.png new file mode 100644 index 0000000..3de34a0 Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/logo-white.png differ diff --git a/Submodules/IntelOwl/frontend/public/manifest.json b/Submodules/IntelOwl/frontend/public/manifest.json new file mode 100644 index 0000000..855630f --- /dev/null +++ b/Submodules/IntelOwl/frontend/public/manifest.json @@ -0,0 +1,32 @@ +{ + "short_name": "IntelOwl", + "name": "IntelOwl", + "description": "Analyze files, domains, IPs in multiple ways from a single API at scale.", + "theme_color": "#0b2b38", + "background_color": "#00161f", + "display": "standalone", + "orientation": "portrait", + "start_url": ".", + "icons": [ + { + "src": "icons/favicon.ico", + "sizes": "48x48 32x32 16x16", + "type": "image/x-icon" + }, + { + "src": "icons/192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/512x512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "logo-blue.png", + "type": "image/png", + "sizes": "4614x4092" + } + ] +} diff --git a/Submodules/IntelOwl/frontend/public/notification.mp3 b/Submodules/IntelOwl/frontend/public/notification.mp3 new file mode 100644 index 0000000..eada5fa Binary files /dev/null and b/Submodules/IntelOwl/frontend/public/notification.mp3 differ diff --git a/Submodules/IntelOwl/frontend/public/robots.txt b/Submodules/IntelOwl/frontend/public/robots.txt new file mode 100644 index 0000000..01b0f9a --- /dev/null +++ b/Submodules/IntelOwl/frontend/public/robots.txt @@ -0,0 +1,2 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * diff --git a/Submodules/IntelOwl/frontend/src/App.jsx b/Submodules/IntelOwl/frontend/src/App.jsx new file mode 100644 index 0000000..d17f992 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/App.jsx @@ -0,0 +1,20 @@ +import React from "react"; +import { BrowserRouter } from "react-router-dom"; +import { GuideProvider } from "./contexts/GuideContext"; + +// layout +import AppMain from "./layouts/AppMain"; +import GuideWrapper from "./components/GuideWrapper"; + +function App() { + return ( + + + + + + + ); +} + +export default App; diff --git a/Submodules/IntelOwl/frontend/src/components/GuideWrapper.jsx b/Submodules/IntelOwl/frontend/src/components/GuideWrapper.jsx new file mode 100644 index 0000000..db9a540 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/GuideWrapper.jsx @@ -0,0 +1,308 @@ +import React from "react"; +import Joyride from "react-joyride"; +import { Outlet, useNavigate } from "react-router-dom"; +import { useMount } from "react-use"; +import { useGuideContext } from "../contexts/GuideContext"; + +export default function GuideWrapper() { + const { guideState, setGuideState } = useGuideContext(); + const navigate = useNavigate(); + + const steps = [ + { + target: "#home__bgImg", + content: ( +
+

Guide

+

+ Welcome to IntelOwls Guide for First Time Visitors! For further + questions you could either check out our{" "} + docs or + reach us out on{" "} + + the official IntelOwl slack channel + +

+
+ ), + disableBeacon: true, + }, + { + target: "#Analyzers", + content: ( +
+

Plugins

+
+

+ Plugins are the core modular components of IntelOwl that can be + easily added, changed and customized. The most important ones are + the Analyzers that allow to perform data extraction on the + observables and/or files that you would like to analyze. +

+
+ ), + disableBeacon: true, + }, + { + target: "#pluginconfigbutton", + content: ( +
+

Plugin Configurations

+

Write up your own plugin configuration!

+

+ Note: Some plugins work out-of-the-box, while others requires to be + configured (with API keys for instance). +

+
+ ), + disableBeacon: true, + }, + { + target: "#scanpage", + content: ( +
+

Scan Page

+

+ You could get started with analyzing various observables with just + three steps{" "} +

+
+ ), + disableBeacon: true, + }, + { + target: "#selectobservable", + content: ( +
+

Select/Add Observables

+
+ ), + disableBeacon: true, + }, + { + target: "#selectplugins", + content: ( +
+

+ Select a Playbook. +
Playbooks are designed to be easy to share sequence of + running Plugins (Analyzers/, Connectors, ...) on a particular kind + of observable. +

+
+ ), + disableBeacon: true, + }, + { + target: "#startScan", + content: ( +
+

Click to Start the Scan

+
+ ), + disableBeacon: true, + }, + { + target: "#Jobs", + content: ( +
+

Jobs History

+

+ Jobs are simple analysis of an observable or a file. Here you could + see the list of all previous jobs and expand over the details + through clicking that particular job from the table +

+
+ ), + disableBeacon: true, + }, + { + target: "#Investigations", + content: ( +
+

Investigations History

+

+ Investigations are a framework to connect jobs with each other. Here + you could see the list of all previous investigations and expand + over the details through clicking that particular investigation from + the table +

+
+ ), + disableBeacon: true, + }, + { + target: "#Dashboard_title", + content: ( +
+

Dashboard

+

See previous job details here with charts and more

+
+ ), + disableBeacon: true, + }, + { + target: "#Dashboard_timepicker", + content: ( +
+

Filter

+

Filter by time to get details about previous jobs

+
+ ), + disableBeacon: true, + }, + ]; + + useMount(() => { + setGuideState({ + steps, + }); + }); + + const handleCallback = (data) => { + const { action, index, _lifecycle, type } = data; + + switch (index) { + case 0: + if (type === "step:after") { + setGuideState({ run: false, stepIndex: 1 }); + navigate("/plugins"); + } + break; + case 1: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 2 }); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 2: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: false, stepIndex: 3 }); + navigate("/scan"); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 3: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 4 }); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 4: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 5 }); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 5: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 6 }); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 6: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 7 }); + navigate("/history/jobs"); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 7: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 8 }); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 8: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 9 }); + navigate("/dashboard"); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 9: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 10 }); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + case 10: + if (type === "step:after") { + if (action === "close") { + setGuideState({ run: true, stepIndex: 11 }); + navigate("/"); + } else { + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + } + } + break; + default: + setGuideState({ run: false, stepIndex: 0 }); + navigate("/"); + break; + } + }; + + return ( + <> + + + + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/History.jsx b/Submodules/IntelOwl/frontend/src/components/History.jsx new file mode 100644 index 0000000..a398976 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/History.jsx @@ -0,0 +1,97 @@ +import React, { Suspense } from "react"; +import { RiFileListFill } from "react-icons/ri"; +import { DiGitMerge } from "react-icons/di"; +import { BsFillPlusCircleFill } from "react-icons/bs"; +import { Button, Col } from "reactstrap"; +import { RouterTabs, FallBackLoading } from "@certego/certego-ui"; +import { useNavigate, useLocation } from "react-router-dom"; + +import { useGuideContext } from "../contexts/GuideContext"; +import { createInvestigation } from "./investigations/result/investigationApi"; + +const JobsTable = React.lazy(() => import("./jobs/table/JobsTable")); +const InvestigationsTable = React.lazy( + () => import("./investigations/table/InvestigationsTable"), +); +/* +lazy imports to enable code splitting +*/ + +const historyRoutes = [ + { + key: "history-jobs", + location: "jobs", + Title: () => ( + + +  Jobs + + ), + Component: () => ( + }> + + + ), + }, + { + key: "history-investigations", + location: "investigations", + Title: () => ( + + +  Investigations + + ), + Component: () => ( + }> + + + ), + }, +]; + +export default function History() { + const navigate = useNavigate(); + const location = useLocation(); + const isJobsTablePage = location?.pathname.includes("jobs"); + + const { guideState, setGuideState } = useGuideContext(); + + React.useEffect(() => { + if (guideState.tourActive) { + setTimeout(() => { + setGuideState({ run: true, stepIndex: 7 }); + }, 200); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const onClick = async () => { + if (isJobsTablePage) { + navigate("/scan"); + } else { + try { + const investigationId = await createInvestigation(); + if (investigationId) navigate(`/investigation/${investigationId}`); + } catch { + // handle inside createInvestigation + } + } + }; + + const createButton = ( + + + + ); + return ; +} diff --git a/Submodules/IntelOwl/frontend/src/components/Routes.jsx b/Submodules/IntelOwl/frontend/src/components/Routes.jsx new file mode 100644 index 0000000..7a4abc8 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/Routes.jsx @@ -0,0 +1,224 @@ +import React, { Suspense } from "react"; +import { FallBackLoading } from "@certego/certego-ui"; +import { Navigate, useParams } from "react-router-dom"; + +import AuthGuard from "../wrappers/AuthGuard"; +import IfAuthRedirectGuard from "../wrappers/IfAuthRedirectGuard"; +import { JobResultSections } from "../constants/miscConst"; + +const Home = React.lazy(() => import("./home/Home")); +const Login = React.lazy(() => import("./auth/Login")); +const Logout = React.lazy(() => import("./auth/Logout")); +const Register = React.lazy(() => import("./auth/Register")); +const EmailVerification = React.lazy(() => import("./auth/EmailVerification")); +const ResetPassword = React.lazy(() => import("./auth/ResetPassword")); +const Organization = React.lazy(() => import("./organization/Organization")); +const TokenPage = React.lazy(() => import("./user/token/TokenPage")); +const JobResult = React.lazy(() => import("./jobs/result/JobResult")); +const CommentResult = React.lazy( + () => import("./jobs/result/bar/comment/CommentResult"), +); +const PluginsContainer = React.lazy(() => import("./plugins/PluginsContainer")); +const Dashboard = React.lazy(() => import("./dashboard/Dashboard")); +const ScanForm = React.lazy(() => import("./scan/ScanForm")); +const UserConfig = React.lazy(() => import("./user/config/UserConfig")); +const ChangePassword = React.lazy(() => import("./auth/ChangePassword")); +const InvestigationResult = React.lazy( + () => import("./investigations/result/InvestigationResult"), +); +const History = React.lazy(() => import("./History")); +/* +lazy imports to enable code splitting +*/ + +function JobRedirect() { + const params = useParams(); + const { id } = params; + return ( + + ); +} + +// public components +const publicRoutesLazy = [ + { + index: true, + element: ( + }> + + + ), + }, +].map((routes) => ({ + ...routes, + element: }>{routes.element}, +})); + +// no auth public components +const noAuthRoutesLazy = [ + { + path: "/login", + element: , + }, + { + path: "/register", + element: , + }, + { + path: "/verify-email", + element: , + }, + { + path: "/reset-password", + element: , + }, +].map((routes) => ({ + ...routes, + element: ( + + }>{routes.element} + + ), +})); + +// auth components +const authRoutesLazy = [ + /* auth */ + { + path: "/logout", + element: ( + }> + + + ), + }, + + { + path: "/change-password", + element: ( + }> + + + ), + }, + /* User/Organization */ + { + path: "/me/organization/*", + element: ( + }> + + + ), + }, + /* CustomConfig */ + { + path: "/me/config/*", + element: ( + }> + + + ), + }, + /* API Access */ + { + path: "/me/api", + element: ( + }> + + + ), + }, + /* Jobs */ + // this is needed for retrocompatibility + { + path: `/jobs/:id`, + element: ( + }> + + + ), + }, + // this is needed from start scan: we don't know visualizers before enter in the job + { + path: `/jobs/:id/:section`, + element: ( + }> + + + ), + }, + { + /* + ex: jobs/1/visualizer/DNS + ex: jobs/1/raw/analyzers + */ + path: `/jobs/:id/:section/:subSection`, + element: ( + }> + + + ), + }, + { + path: "/jobs/:id/comments", + element: ( + }> + + + ), + }, + /* History */ + { + path: "/history/*", + element: ( + }> + + + ), + }, + /* Investigation */ + { + path: `/investigation/:id`, + element: ( + }> + + + ), + }, + /* Plugins */ + { + path: "/plugins/*", + element: ( + }> + + + ), + }, + /* Dashboard */ + { + path: "/dashboard", + element: ( + }> + + + ), + }, + /* Scan */ + { + path: "/scan", + element: ( + }> + + + ), + }, +].map((routes) => ({ + ...routes, + element: ( + + }>{routes.element} + + ), +})); + +export { publicRoutesLazy, noAuthRoutesLazy, authRoutesLazy }; diff --git a/Submodules/IntelOwl/frontend/src/components/auth/ChangePassword.jsx b/Submodules/IntelOwl/frontend/src/components/auth/ChangePassword.jsx new file mode 100644 index 0000000..a877689 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/ChangePassword.jsx @@ -0,0 +1,131 @@ +import React from "react"; +import { + FormGroup, + Label, + Container, + Input, + Spinner, + Button, +} from "reactstrap"; +import { Form, Formik } from "formik"; +import useTitle from "react-use/lib/useTitle"; + +import { addToast, ContentSection } from "@certego/certego-ui"; + +import { useAuthStore } from "../../stores/useAuthStore"; + +const initialValues = { + old_password: "", + new_password: "", + confirmNewPassword: "", +}; + +const validateForm = (values) => { + const errors = {}; + if (!values.old_password) { + errors.old_password = "Required"; + } + if (!values.new_password) { + errors.new_password = "Required"; + } + if (!values.confirmNewPassword) { + errors.confirmNewPassword = "Required"; + } else if (values.confirmNewPassword !== values.new_password) { + errors.confirmNewPassword = "Passwords do not match"; + } + return errors; +}; + +// Component +export default function ChangePassword() { + // page title + useTitle("IntelOwl | Change Password", { restoreOnUnmount: true }); + + // auth store + const changePassword = useAuthStore( + React.useCallback((state) => state.service.changePassword, []), + ); + + // callback + const onSubmit = React.useCallback( + async (values, { setSubmitting }) => { + try { + await changePassword(values); + addToast("Password changed successfully!", null, "success"); + } catch (error) { + addToast("Failed to change password", error.parsedMsg, "danger", true); + } finally { + setSubmitting(false); + } + }, + [changePassword], + ); + + return ( + + +

Change Password

+
+ + {(formik) => ( +
+ + + + + + + + + + + + + + + +
+ )} +
+
+
+ ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/EmailVerification.jsx b/Submodules/IntelOwl/frontend/src/components/auth/EmailVerification.jsx new file mode 100644 index 0000000..04a6765 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/EmailVerification.jsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Navigate, useSearchParams } from "react-router-dom"; +import { Spinner } from "reactstrap"; +import { ContentSection } from "@certego/certego-ui"; + +import { verifyEmail } from "./authApi"; + +// Component +export default function EmailVerification() { + console.debug("EmailVerification rendered!"); + + // search param + const [searchParams] = useSearchParams(); + const key = searchParams.get("key") || null; + console.debug("key:", key); + + // local state + const [isKeyValid, setIsKeyValid] = React.useState(true); + const [isVerified, setIsVerified] = React.useState(false); + + // side-effects + React.useEffect(() => { + if (key) { + setTimeout( + () => + verifyEmail({ key }) + .then(() => setIsVerified(true)) + .catch(() => setIsKeyValid(false)), + 500, + ); + } else { + setIsKeyValid(false); + } + }, [key]); + + return isVerified ? ( + + ) : ( + + {isKeyValid ? ( +
+ +

Verifying...

+
+ ) : ( +
Error: Invalid key.
+ )} +
+ ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/Login.jsx b/Submodules/IntelOwl/frontend/src/components/auth/Login.jsx new file mode 100644 index 0000000..f3846c6 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/Login.jsx @@ -0,0 +1,244 @@ +import axios from "axios"; +import React from "react"; +import { AiOutlineInfoCircle } from "react-icons/ai"; +import { + FormGroup, + Label, + Container, + Input, + Spinner, + Button, + Row, + Tooltip, +} from "reactstrap"; +import { Form, Formik } from "formik"; +import useTitle from "react-use/lib/useTitle"; + +import { addToast, ContentSection } from "@certego/certego-ui"; +import { AUTH_BASE_URI } from "../../constants/apiURLs"; + +import { PUBLIC_URL, RECAPTCHA_SITEKEY } from "../../constants/environment"; +import ReCAPTCHAInput from "./utils/ReCAPTCHAInput"; +import { RecaptchaValidator } from "./utils/validator"; +import { ConfigurationModalAlert } from "./utils/registration-alert"; +import { useAuthStore } from "../../stores/useAuthStore"; +import { checkConfiguration } from "./authApi"; + +import { + ResendVerificationEmailButton, + ForgotPasswordButton, +} from "./utils/registration-buttons"; + +// constants +const initialValues = { + username: "", + password: "", + recaptcha: "noKey", +}; +// methods +const onValidate = (values) => { + const errors = {}; + if (!values.username) { + errors.username = "Required"; + } + if (!values.password) { + errors.password = "Required"; + } + // recaptcha + const recaptchaErrors = RecaptchaValidator(values.recaptcha); + if (recaptchaErrors.recaptcha) { + errors.recaptcha = recaptchaErrors.recaptcha; + } + return errors; +}; + +// Component +export default function Login() { + console.debug("Login rendered!"); + + const [isOpen, setIsOpen] = React.useState(false); + + // page title + useTitle("IntelOwl | Login", { restoreOnUnmount: true }); + + // local state + const [passwordShown, setPasswordShown] = React.useState(false); + const [showConfigurationModal, setShowConfigurationModal] = + React.useState(false); + + React.useEffect(() => { + checkConfiguration({ + params: { + page: "login", + }, + }).catch(() => { + setShowConfigurationModal(true); + }); + }, []); + + console.debug("showConfigurationModal:", showConfigurationModal); + + // auth store + const loginUser = useAuthStore( + React.useCallback((state) => state.service.loginUser, []), + ); + + // callbacks + const onSubmit = React.useCallback( + async (values, _formik) => { + try { + await loginUser(values); + } catch (error) { + // handled inside loginUser + } + }, + [loginUser], + ); + + return ( + + {showConfigurationModal && ( + + )} + +
+ IntelOwl Logo +
+ + +

Log In

+ +
+
+ {/* Form */} + + {(formik) => ( +
+ {/* username */} + + + + + {/* password */} + + + + + + setPasswordShown(!passwordShown)} + /> + + + {/* reCAPTCHA */} + + {RECAPTCHA_SITEKEY && ( + + )} + + {/* Submit */} + + + +
+ )} +
+
+ {/* popover buttons */} + + + + +
+
+ ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/Logout.jsx b/Submodules/IntelOwl/frontend/src/components/auth/Logout.jsx new file mode 100644 index 0000000..deafedb --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/Logout.jsx @@ -0,0 +1,20 @@ +import React from "react"; + +import { FallBackLoading } from "@certego/certego-ui"; + +import { useAuthStore } from "../../stores/useAuthStore"; + +export default function Logout() { + // auth store + const [loading, logoutUser] = useAuthStore( + React.useCallback((state) => [state.loading, state.service.logoutUser], []), + ); + + React.useEffect(() => { + if (!loading) { + logoutUser(); + } + }, [loading, logoutUser]); + + return ; +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/Register.jsx b/Submodules/IntelOwl/frontend/src/components/auth/Register.jsx new file mode 100644 index 0000000..db84569 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/Register.jsx @@ -0,0 +1,561 @@ +import React from "react"; +import { + Container, + Row, + Col, + FormGroup, + Label, + Spinner, + Input, + InputGroup, + Button, + InputGroupText, +} from "reactstrap"; +import { Form, Formik } from "formik"; +import useTitle from "react-use/lib/useTitle"; + +import { ContentSection, Select } from "@certego/certego-ui"; + +import { PUBLIC_URL, RECAPTCHA_SITEKEY } from "../../constants/environment"; +import ReCAPTCHAInput from "./utils/ReCAPTCHAInput"; +import { + AfterRegistrationModalAlert, + InviteOnlyAlert, + ConfigurationModalAlert, +} from "./utils/registration-alert"; +import { registerUser, checkConfiguration } from "./authApi"; +import { + EmailValidator, + PasswordValidator, + RecaptchaValidator, + UserFieldsValidator, + ProfileValidator, + UsernameValidator, + ComparePassword, +} from "./utils/validator"; + +// constants +const hearAboutUsChoices = [ + { + label: "Other", + value: "other", + }, + { + label: "Search Engine (Google, DuckDuckGo, etc.)", + value: "search_engine", + }, + { + label: "Recommended by friend or colleague", + value: "was_recommended", + }, + { + label: "Social media", + value: "social_media", + }, + { + label: "Blog or Publication", + value: "blog_or_publication", + }, +]; + +const INITIAL_VALUES = { + first_name: "", + last_name: "", + email: "", + username: "", + password: "", + confirmPassword: "", + company_name: "", + company_role: "", + twitter_handle: "", + discover_from: "other", + recaptcha: "noKey", +}; + +const REGISTRATION_FORM_STORAGE_KEY = "registrationForm"; +const initialValues = + JSON.parse(localStorage.getItem(REGISTRATION_FORM_STORAGE_KEY, "{}")) || + INITIAL_VALUES; + +console.debug("initialValues", initialValues); + +const onValidate = (values) => { + const errors = {}; + + // static text fields + ["first_name", "last_name"].forEach((field) => { + const userErrors = UserFieldsValidator(field, values[field]); + if (userErrors[field]) { + errors[field] = userErrors[field]; + } + }); + + // username + const usernameErrors = UsernameValidator(values.username); + if (usernameErrors.username) { + errors.username = usernameErrors.username; + } + + // email + const emailErrors = EmailValidator(values.email); + if (emailErrors.email) { + errors.email = emailErrors.email; + } + + // profile + ["company_name", "company_role"].forEach((field) => { + const profileErrors = ProfileValidator(field, values[field]); + if (profileErrors[field]) { + errors[field] = profileErrors[field]; + } + }); + + // store in localStorage so user doesn't have to fill all fields again + localStorage.setItem( + REGISTRATION_FORM_STORAGE_KEY, + JSON.stringify({ + ...values, + password: "", + confirmPassword: "", + recaptcha: "noKey", + }), + ); + Object.keys(initialValues).forEach((key) => { + initialValues[key] = values[key]; + }); + + // password fields + const passwordErrors = PasswordValidator(values.password); + if (passwordErrors.password) { + errors.password = passwordErrors.password; + } + const confirmPasswordErrors = PasswordValidator(values.confirmPassword); + if (confirmPasswordErrors.password) { + errors.confirmPassword = confirmPasswordErrors.password; + } + const comparePasswordErrors = ComparePassword( + values.password, + values.confirmPassword, + ); + if (comparePasswordErrors.password) { + errors.password = comparePasswordErrors.password; + } + if (comparePasswordErrors.confirmPassword) { + errors.confirmPassword = comparePasswordErrors.confirmPassword; + } + + // recaptcha + const recaptchaErrors = RecaptchaValidator(values.recaptcha); + if (recaptchaErrors.recaptcha) { + errors.recaptcha = recaptchaErrors.recaptcha; + } + + console.debug("Errors", errors); + return errors; +}; + +// Component +export default function Register() { + console.debug("Register rendered!"); + + // page title + useTitle("IntelOwl | Sign up", { restoreOnUnmount: true }); + + // local state + const [showAfterRegistrationModal, setShowAfterRegistrationModal] = + React.useState(false); + const [showConfigurationModal, setShowConfigurationModal] = + React.useState(false); + const [passwordShown, setPasswordShown] = React.useState(false); + + console.debug("showAfterRegistrationModal:", showAfterRegistrationModal); + + React.useEffect(() => { + checkConfiguration({ + params: { + page: "register", + }, + }).catch(() => { + setShowConfigurationModal(true); + }); + }, []); + + console.debug("showConfigurationModal:", showConfigurationModal); + // callbacks + const onSubmit = React.useCallback( + async (values) => { + const reqBody = { + first_name: values.first_name, + last_name: values.last_name, + username: values.username, + email: values.email, + password: values.password, + recaptcha: values.recaptcha, + profile: { + company_name: values.company_name, + company_role: values.company_role, + twitter_handle: values.twitter_handle, + discover_from: values.discover_from, + }, + }; + try { + await registerUser(reqBody); + + // deleted user data after successful registration + localStorage.removeItem(REGISTRATION_FORM_STORAGE_KEY); + Object.keys(INITIAL_VALUES).forEach((key) => { + initialValues[key] = INITIAL_VALUES[key]; + }); + + setShowAfterRegistrationModal(true); + } catch (error) { + // handled inside registerUser + } + }, + [setShowAfterRegistrationModal], + ); + + return ( + + {showConfigurationModal && ( + + )} + {showAfterRegistrationModal && ( + + )} + + {/* IntelOwl Logo */} + + IntelOwl Logo + + + + + + +

Register

+
+
+ {/* Form */} + + {(formik) => ( +
+ {/* Name */} + + + + + {formik.touched.first_name && ( + {formik.errors.first_name} + )} + + + + + {formik.touched.last_name && ( + {formik.errors.last_name} + )} + + + {/* Email/Username */} + + + + + {formik.touched.email && ( + {formik.errors.email} + )} + + + + + {formik.touched.username && ( + {formik.errors.username} + )} + + + {/* Password */} + + + + + {formik.touched.password && ( + {formik.errors.password} + )} + + + + + {formik.touched.confirmPassword && ( + {formik.errors.confirmPassword} + )} + + + + setPasswordShown(!passwordShown)} + /> + + + + We ask you to provide the following information to better + understand what you intend to use IntelOwl for + + {/* Extra fields */} + + + + + {formik.touched.company_name && ( + {formik.errors.company_name} + )} + + + + + {formik.touched.company_role && ( + {formik.errors.company_role} + )} + + + + + + + @ + + + + + + + {formik.touched.password && ( + {formik.errors.password} + )} + + + + + + + {formik.touched.confirmPassword && ( + {formik.errors.confirmPassword} + )} + + + + setPasswordShown(!passwordShown)} + /> + + + {/* reCAPTCHA */} + + {RECAPTCHA_SITEKEY && ( + + )} + + {/* Submit */} + + + +
+ )} +
+
+ ) : ( +
Error: Invalid key.
+ )} +
+ ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/authApi.js b/Submodules/IntelOwl/frontend/src/components/auth/authApi.js new file mode 100644 index 0000000..26224da --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/authApi.js @@ -0,0 +1,81 @@ +import axios from "axios"; + +import { addToast } from "@certego/certego-ui"; + +import { AUTH_BASE_URI } from "../../constants/apiURLs"; + +export async function registerUser(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/register`, body); + return resp; + } catch (err) { + addToast("Registration failed!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function verifyEmail(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/verify-email`, body); + addToast( + "Your email has been succesfully verified!", + null, + "success", + true, + ); + return resp; + } catch (err) { + addToast("Email verification failed!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function resendVerificationMail(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/resend-verification`, body); + addToast("Verification email sent!", null, "success"); + return resp; + } catch (err) { + addToast("Failed to send email!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function requestPasswordReset(body) { + try { + const resp = await axios.post( + `${AUTH_BASE_URI}/request-password-reset`, + body, + ); + addToast("Email sent!", null, "success"); + return resp; + } catch (err) { + addToast("Failed to send email!", err.parsedMsg, "danger", true); + return null; + } +} + +export async function resetPassword(body) { + try { + const resp = await axios.post(`${AUTH_BASE_URI}/reset-password`, body); + addToast("Password reset successfully!", null, "success", true); + return resp; + } catch (err) { + addToast("Password reset failed!", err.parsedMsg, "danger", true); + return Promise.reject(err); + } +} + +export async function checkConfiguration(body) { + try { + const resp = await axios.get(`${AUTH_BASE_URI}/configuration`, body); + const { errors } = resp.data; + if (errors) { + return Promise.reject(errors); + } + return resp; + } catch (err) { + // this API always return 200, this catch should never ne triggered, but it's better to be safe + return Promise.reject(err); + } +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/utils/EmailForm.jsx b/Submodules/IntelOwl/frontend/src/components/auth/utils/EmailForm.jsx new file mode 100644 index 0000000..8c1162a --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/utils/EmailForm.jsx @@ -0,0 +1,103 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { FormGroup, Label, Input, Button, Spinner } from "reactstrap"; +import { Form, Formik } from "formik"; + +import ReCAPTCHAInput from "./ReCAPTCHAInput"; +import { RECAPTCHA_SITEKEY } from "../../../constants/environment"; +import { RecaptchaValidator, EmailValidator } from "./validator"; + +// constants +const initialValues = { + email: "", + recaptcha: "noKey", +}; + +// methods +const onValidate = (values) => { + const errors = {}; + // email + const emailErrors = EmailValidator(values.email); + if (emailErrors.email) { + errors.email = emailErrors.email; + } + // recaptcha + const recaptchaErrors = RecaptchaValidator(values.recaptcha); + if (recaptchaErrors.recaptcha) { + errors.recaptcha = recaptchaErrors.recaptcha; + } + return errors; +}; + +// Resend Verification Email Form +export default function EmailForm({ onFormSubmit, apiCallback, ...restProps }) { + // callbacks + const onSubmit = React.useCallback( + async (values) => { + try { + await apiCallback(values); + onFormSubmit(); + } catch (error) { + // error will be handled by apiCallback + } + }, + [apiCallback, onFormSubmit], + ); + + return ( + + {(formik) => ( +
+ {/* Email field */} + + + + {formik.touched.email && {formik.errors.email}} + + {/* reCAPTCHA */} + + {RECAPTCHA_SITEKEY && ( + + )} + + {/* Submit */} + + + +
+ )} +
+ ); +} + +EmailForm.propTypes = { + onFormSubmit: PropTypes.func.isRequired, + apiCallback: PropTypes.func.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/auth/utils/ReCAPTCHAInput.jsx b/Submodules/IntelOwl/frontend/src/components/auth/utils/ReCAPTCHAInput.jsx new file mode 100644 index 0000000..bd6b2d0 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/utils/ReCAPTCHAInput.jsx @@ -0,0 +1,39 @@ +import React from "react"; +import ReCAPTCHA from "react-google-recaptcha"; +import usePrevious from "react-use/lib/usePrevious"; +import { useFormikContext } from "formik"; + +import { RECAPTCHA_SITEKEY } from "../../../constants/environment"; + +export default function ReCAPTCHAInput(props) { + const { isSubmitting, setFieldValue } = useFormikContext(); + + // refs + const recaptchaRef = React.useRef(); + + // callbacks + const onChange = React.useCallback( + (value) => setFieldValue("recaptcha", value), + [setFieldValue], + ); + + // side-effect + const prevIsSubmitting = usePrevious(isSubmitting); + + React.useEffect(() => { + const wasSubmitted = prevIsSubmitting === true && isSubmitting === false; + if (wasSubmitted) { + recaptchaRef.current.reset(); + setFieldValue("recaptcha", null); + } + }, [recaptchaRef, setFieldValue, prevIsSubmitting, isSubmitting]); + + return ( + + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/utils/registration-alert.jsx b/Submodules/IntelOwl/frontend/src/components/auth/utils/registration-alert.jsx new file mode 100644 index 0000000..dafa8b8 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/utils/registration-alert.jsx @@ -0,0 +1,137 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Modal, ModalHeader, ModalBody, Alert } from "reactstrap"; +import { MdInfoOutline } from "react-icons/md"; +import { useNavigate } from "react-router-dom"; +import { INTELOWL_DOCS_URL } from "../../../constants/environment"; + +export function InviteOnlyAlert() { + return ( + +
+ +  Sign up below to join the waitlist! +
+

+ Please note that IntelOwl is operated as an invite-only trust group. + Once you sign up, our team will reach out to you at the provided email + address. +
+ + We recommend signing up with a business email address and not a + personal one to increase your chances of getting access. + +

+
+ ); +} + +export function AfterRegistrationModalAlert(props) { + // modal state from props + const { isOpen, setIsOpen } = props; + const navigate = useNavigate(); + + // callbacks + const toggle = React.useCallback(() => { + navigate("/"); + setIsOpen((open) => !open); + }, [navigate, setIsOpen]); + + return ( + + Registration successful! 🥳 + + <> +
+ +

Thank you for signing up on IntelOwl! 🤝

+
+
+
+ + Next Steps: + +
    +
  1. + Verify your email address. We have already sent you a{" "} + + link + + . +
  2. +
  3. Our team will reach out to you soon afterwards.
  4. +
+
+ +
+
+ ); +} + +export function ConfigurationModalAlert(props) { + const { isOpen, setIsOpen, title } = props; + const navigate = useNavigate(); + + // callbacks + const toggle = React.useCallback(() => { + navigate("/"); + setIsOpen((open) => !open); + }, [navigate, setIsOpen]); + + return ( + + Warning + + <> +
+ +

{title}

+
+
+
+

+ If you are an admin please check the{" "} + + documentation + {" "} + and correctly configure all the required variables. +

+
+ +
+
+ ); +} + +AfterRegistrationModalAlert.propTypes = { + isOpen: PropTypes.bool.isRequired, + setIsOpen: PropTypes.func.isRequired, +}; + +ConfigurationModalAlert.propTypes = { + isOpen: PropTypes.bool.isRequired, + setIsOpen: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/auth/utils/registration-buttons.jsx b/Submodules/IntelOwl/frontend/src/components/auth/utils/registration-buttons.jsx new file mode 100644 index 0000000..f8fdf04 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/utils/registration-buttons.jsx @@ -0,0 +1,87 @@ +import React from "react"; +// import PropTypes from "prop-types"; +import { Alert, PopoverBody } from "reactstrap"; +import { IoMail } from "react-icons/io5"; +import { PopupFormButton } from "@certego/certego-ui"; +import EmailForm from "./EmailForm"; +import { resendVerificationMail, requestPasswordReset } from "../authApi"; + +function Icon(text) { + return ( + + +   + {text} + + ); +} + +function FormPopoverBody(formProps, text, api) { + return ( + + + +  {text} + + + + ); +} + +function EmailIcon() { + return Icon("Need Verification Email?"); +} + +function EmailFormPopoverBody(formProps) { + return FormPopoverBody( + formProps, + "We will shoot you an email with instructions to verify your email address.", + resendVerificationMail, + ); +} + +// Popover Button for "Request Verification Email?" +export function ResendVerificationEmailButton() { + return ( + + ); +} + +function PasswordIcon() { + return Icon("Forgot Password?"); +} + +function PasswordFormPopoverBody(formProps) { + return FormPopoverBody( + formProps, + "We will shoot you an email with instructions to reset your password.", + requestPasswordReset, + ); +} + +// Popover Button for "Forgot Password?" +export function ForgotPasswordButton() { + return ( + + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/auth/utils/validator.jsx b/Submodules/IntelOwl/frontend/src/components/auth/utils/validator.jsx new file mode 100644 index 0000000..db91007 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/auth/utils/validator.jsx @@ -0,0 +1,83 @@ +import { PASSWORD_REGEX, EMAIL_REGEX } from "../../../constants/regexConst"; +import { RECAPTCHA_SITEKEY } from "../../../constants/environment"; +import { HACKER_MEME_STRING } from "../../../constants/miscConst"; + +export function ComparePassword(password, confirmPassword) { + const errors = {}; + if ( + password.length > 0 && + confirmPassword.length > 0 && + password !== confirmPassword + ) { + errors.password = "Passwords do not match."; + errors.confirmPassword = "Passwords do not match."; + } + return errors; +} + +export function PasswordValidator(password) { + const errors = {}; + if (!password) { + errors.password = "Required"; + } else if (password.length < 12) { + errors.password = "Must be 12 characters or more"; + } else if (!PASSWORD_REGEX.test(password)) { + errors.password = + "The password is entirely numeric or contains special characters"; + } + return errors; +} + +export function UserFieldsValidator(field, value) { + const errors = {}; + // text fields + if (!value) { + errors[field] = "Required"; + } else if (value.length > 15) { + errors[field] = "Must be 15 characters or less"; + } else if (value.length < 4) { + errors[field] = "Must be 4 characters or more"; + } + return errors; +} + +export function UsernameValidator(username) { + const errors = UserFieldsValidator("username", username); + if ( + ["administrator", "admin", "certego", "hacker"].indexOf(username) !== -1 + ) { + errors.username = HACKER_MEME_STRING; + } + return errors; +} + +export function ProfileValidator(field, value) { + const errors = {}; + // text fields + if (!value) { + errors[field] = "Required"; + } else if (value.length > 30) { + errors[field] = "Must be 30 characters or less"; + } else if (value.length < 3) { + errors[field] = "Must be 3 characters or more"; + } + return errors; +} + +export function EmailValidator(email) { + const errors = {}; + if (!email) { + errors.email = "Required"; + } else if (!EMAIL_REGEX.test(email)) { + errors.email = "Invalid email address"; + } + return errors; +} + +export function RecaptchaValidator(recaptcha) { + const errors = {}; + if (recaptcha === "noKey" && RECAPTCHA_SITEKEY) { + errors.recaptcha = "Required"; + } + return errors; +} diff --git a/Submodules/IntelOwl/frontend/src/components/common/JobTag.jsx b/Submodules/IntelOwl/frontend/src/components/common/JobTag.jsx new file mode 100644 index 0000000..2702746 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/JobTag.jsx @@ -0,0 +1,26 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Badge } from "reactstrap"; + +export function JobTag(props) { + const { tag, ...rest } = props; + + return ( + + {tag.label} + + ); +} + +JobTag.propTypes = { + tag: PropTypes.shape({ + label: PropTypes.string.isRequired, + color: PropTypes.string.isRequired, + }).isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/PlaybookTag.jsx b/Submodules/IntelOwl/frontend/src/components/common/PlaybookTag.jsx new file mode 100644 index 0000000..69bdbeb --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/PlaybookTag.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Badge } from "reactstrap"; + +export function PlaybookTag(props) { + const { playbook, ...rest } = props; + + return ( + + {playbook} + + ); +} + +PlaybookTag.propTypes = { + playbook: PropTypes.string.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/StatusTag.jsx b/Submodules/IntelOwl/frontend/src/components/common/StatusTag.jsx new file mode 100644 index 0000000..b0dbee6 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/StatusTag.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; +import { StatusColors } from "../../constants/colorConst"; + +export function StatusTag(props) { + const { status, className, ...rest } = props; + + const statusLower = status.toLowerCase(); + + const color = StatusColors?.[statusLower] || "light"; + const divClass = classnames(`bg-${color}`, className); + + return ( +
+ + {status.toUpperCase().replaceAll("_", " ")} + +
+ ); +} + +StatusTag.propTypes = { + status: PropTypes.string.isRequired, + className: PropTypes.string, +}; + +StatusTag.defaultProps = { + className: null, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/TLPTag.jsx b/Submodules/IntelOwl/frontend/src/components/common/TLPTag.jsx new file mode 100644 index 0000000..9be82bc --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/TLPTag.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Badge, UncontrolledTooltip } from "reactstrap"; +import { TLPColors } from "../../constants/colorConst"; +import { TLPDescriptions } from "../../constants/miscConst"; +import { TlpChoices } from "../../constants/advancedSettingsConst"; + +export function TLPTag(props) { + const { value, ...rest } = props; + const badgeId = `tlptag-badge__${value}`; + const color = TLPColors?.[value] || "#dfe1e2"; + const tooltipText = TLPDescriptions?.[value] || "invalid"; + + return value ? ( + + {value} + + {tooltipText} + + + ) : null; +} + +TLPTag.propTypes = { + value: PropTypes.oneOf(TlpChoices).isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/TableCell.jsx b/Submodules/IntelOwl/frontend/src/components/common/TableCell.jsx new file mode 100644 index 0000000..ba95445 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/TableCell.jsx @@ -0,0 +1,53 @@ +import { CopyToClipboardButton } from "@certego/certego-ui"; +import React from "react"; +import PropTypes from "prop-types"; + +export default function TableCell(props) { + const { + value = null, + isTruncate = false, + isCopyToClipboard = false, + job, + } = props; + + const tableId = job ? `table-user-${job.id}` : `table-user-${value}`; + const tableKey = job ? `table-user-${job.id}` : `table-user-${value}`; + + return ( +
+ {isCopyToClipboard ? ( + + {value} + + ) : ( +
+ + {value} + +
+ )} +
+ ); +} + +TableCell.propTypes = { + value: PropTypes.string, + isTruncate: PropTypes.bool, + isCopyToClipboard: PropTypes.bool, + job: PropTypes.object, +}; + +TableCell.defaultProps = { + value: null, + isTruncate: false, + isCopyToClipboard: false, + job: null, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/TableCellList.jsx b/Submodules/IntelOwl/frontend/src/components/common/TableCellList.jsx new file mode 100644 index 0000000..76c5452 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/TableCellList.jsx @@ -0,0 +1,43 @@ +import { CopyToClipboardButton } from "@certego/certego-ui"; +import React from "react"; +import PropTypes from "prop-types"; + +export default function TableCellList(props) { + const { value = [], idPrefix = null, keyPrefix = null, ulKey = null } = props; + + return ( +
    + {value?.sort().map((val, index) => ( +
  • +
    + + {val} + +
    +
  • + ))} +
+ ); +} + +TableCellList.propTypes = { + value: PropTypes.arrayOf(PropTypes.string), + idPrefix: PropTypes.string, + keyPrefix: PropTypes.string, + ulKey: PropTypes.string, +}; + +TableCellList.defaultProps = { + value: [], + idPrefix: null, + keyPrefix: null, + ulKey: null, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/areYouSureConfirmDialog.jsx b/Submodules/IntelOwl/frontend/src/components/common/areYouSureConfirmDialog.jsx new file mode 100644 index 0000000..d1bebfa --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/areYouSureConfirmDialog.jsx @@ -0,0 +1,24 @@ +import React from "react"; +import { IoMdWarning } from "react-icons/io"; + +import { confirm } from "@certego/certego-ui"; + +export const areYouSureConfirmDialog = (opName) => + confirm({ + title: ( +
+ + Confirm +
+ ), + message: ( +
+
Operation:
+
{opName}
+
+ Are you sure ? +
+ ), + confirmColor: "secondary", + cancelColor: "link text-gray", + }); diff --git a/Submodules/IntelOwl/frontend/src/components/common/icon/StatusIcon.jsx b/Submodules/IntelOwl/frontend/src/components/common/icon/StatusIcon.jsx new file mode 100644 index 0000000..d585400 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/icon/StatusIcon.jsx @@ -0,0 +1,78 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classnames from "classnames"; +import { + MdCircle, + MdOutlinePending, + MdCheckCircleOutline, + MdWarningAmber, + MdOutlineBlock, +} from "react-icons/md"; +import { IoMdCloseCircleOutline } from "react-icons/io"; +import { RiLoader2Fill } from "react-icons/ri"; + +import { UncontrolledTooltip } from "reactstrap"; +import { StatusColors } from "../../../constants/colorConst"; +import { JobFinalStatuses } from "../../../constants/jobConst"; +import { InvestigationStatuses } from "../../../constants/investigationConst"; + +const STATUS_ICON_MAP = { + pending: MdOutlinePending, + running: RiLoader2Fill, + analyzers_running: RiLoader2Fill, + connectors_running: RiLoader2Fill, + pivots_running: RiLoader2Fill, + visualizers_running: RiLoader2Fill, + analyzers_completed: RiLoader2Fill, + connectors_completed: RiLoader2Fill, + pivots_completed: RiLoader2Fill, + visualizers_completed: RiLoader2Fill, + reported_with_fails: MdWarningAmber, + reported_without_fails: MdCheckCircleOutline, + success: MdCheckCircleOutline, + killed: MdOutlineBlock, + failed: IoMdCloseCircleOutline, + concluded: MdCheckCircleOutline, +}; + +export function StatusIcon(props) { + const { status, className, ...rest } = props; + + const statusLower = status.toLowerCase(); + + const color = StatusColors?.[statusLower] || "light"; + const Icon = STATUS_ICON_MAP?.[statusLower] || MdCircle; + const iconClassName = classnames(`text-${color}`, className); + + const statuses = Object.values(JobFinalStatuses).concat( + Object.values(InvestigationStatuses), + ); + + return ( + <> + + {statuses.includes(statusLower) && ( + + {statusLower} + + )} + + ); +} + +StatusIcon.propTypes = { + status: PropTypes.string.isRequired, + className: PropTypes.string, +}; + +StatusIcon.defaultProps = { + className: null, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/common/icon/icons.jsx b/Submodules/IntelOwl/frontend/src/components/common/icon/icons.jsx new file mode 100644 index 0000000..f7e16d5 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/icon/icons.jsx @@ -0,0 +1,65 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Spinner } from "reactstrap"; +import { + MdDeleteOutline, + MdOutlineRefresh, + MdComment, + MdFileDownload, +} from "react-icons/md"; +import { FaRegStopCircle } from "react-icons/fa"; + +// These function are needed in IconButton because it expects Icon as a function + +export function DeleteIcon() { + return ( + + + Delete + + ); +} + +export function CommentIcon({ commentNumber }) { + return ( + + + Comments ({commentNumber}) + + ); +} + +CommentIcon.propTypes = { + commentNumber: PropTypes.number.isRequired, +}; + +export function retryJobIcon() { + return ( + + + Rescan + + ); +} + +export function downloadReportIcon() { + return ( + + + Report + + ); +} + +export function SpinnerIcon() { + return ; +} + +export function killJobIcon() { + return ( + + + Kill job + + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/common/markdownToHtml.jsx b/Submodules/IntelOwl/frontend/src/components/common/markdownToHtml.jsx new file mode 100644 index 0000000..f733925 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/common/markdownToHtml.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import ReactMarkdown from "react-markdown"; + +/** + * @param {string} text + */ +export function markdownToHtml(text) { + return ( + , + // eslint-disable-next-line id-length + a: ({ node: _, ...props }) => ( + // eslint-disable-next-line jsx-a11y/anchor-has-content + + ), + }} + /> + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/dashboard/Dashboard.jsx b/Submodules/IntelOwl/frontend/src/components/dashboard/Dashboard.jsx new file mode 100644 index 0000000..0ef0970 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/dashboard/Dashboard.jsx @@ -0,0 +1,160 @@ +import React, { useState, useEffect } from "react"; +import { Container, Row, Col, ButtonGroup, Button } from "reactstrap"; +import useTitle from "react-use/lib/useTitle"; +import { + ElasticTimePicker, + SmallInfoCard, + useTimePickerStore, +} from "@certego/certego-ui"; + +import { + JobStatusBarChart, + JobTypeBarChart, + JobObsClassificationBarChart, + JobFileMimetypeBarChart, + JobObsNamePieChart, + JobFileHashPieChart, +} from "./utils/charts"; + +import { useGuideContext } from "../../contexts/GuideContext"; +import { useOrganizationStore } from "../../stores/useOrganizationStore"; + +const charts1 = [ + ["JobStatusBarChart", "Job: Status", JobStatusBarChart], + [ + "JobObsNamePieChart", + "Job: Frequent IPs, Hash & Domains", + JobObsNamePieChart, + ], + ["JobFileHashPieChart", "Job: Frequent Files", JobFileHashPieChart], +]; +const charts2 = [ + ["JobTypeBarChart", "Job: Type", JobTypeBarChart], + [ + "JobObsClassificationBarChart", + "Job: Observable Classification", + JobObsClassificationBarChart, + ], + ["JobFileMimetypeBarChart", "Job: File Mimetype", JobFileMimetypeBarChart], +]; + +export default function Dashboard() { + // const isSelectedUI = JobResultSections.VISUALIZER; + const { guideState, setGuideState } = useGuideContext(); + + const [orgState, setOrgState] = useState(() => false); + + useEffect(() => { + if (guideState.tourActive) { + setTimeout(() => { + setGuideState({ run: true, stepIndex: 9 }); + }, 100); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + console.debug("Dashboard rendered!"); + + const { range, onTimeIntervalChange } = useTimePickerStore(); + + // page title + useTitle("IntelOwl | Dashboard", { restoreOnUnmount: true }); + + const { organization } = useOrganizationStore( + React.useCallback( + (state) => ({ + organization: state.organization, + }), + [], + ), + ); + + return ( + +
+
+

+ Dashboard +

+
+
+ {organization?.name ? ( + + + + + ) : null} + + +
+
+ + + {charts1.map(([id, header, Component], index) => ( + + + + + } + style={{ minHeight: 360 }} + /> + + ))} + + + {charts2.map(([id, header, Component]) => ( + + + + + } + style={{ minHeight: 360 }} + /> + + ))} + +
+ ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/dashboard/utils/charts.jsx b/Submodules/IntelOwl/frontend/src/components/dashboard/utils/charts.jsx new file mode 100644 index 0000000..3ca14cc --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/dashboard/utils/charts.jsx @@ -0,0 +1,201 @@ +import React from "react"; +import { Bar } from "recharts"; + +import { + getRandomColorsArray, + AnyChartWidget, + PieChartWidget, +} from "@certego/certego-ui"; + +import { + JobStatusColors, + JobTypeColors, + ObservableClassificationColors, +} from "../../../constants/colorConst"; + +import { + JOB_AGG_STATUS_URI, + JOB_AGG_TYPE_URI, + JOB_AGG_OBS_CLASSIFICATION_URI, + JOB_AGG_FILE_MIMETYPE_URI, + JOB_AGG_OBS_NAME_URI, + JOB_AGG_FILE_MD5_URI, +} from "../../../constants/apiURLs"; + +// constants +const colors = getRandomColorsArray(10, true); + +// bar charts + +export const JobStatusBarChart = React.memo((props) => { + console.debug("JobStatusBarChart rendered!"); + /* eslint-disable */ + var ORG_JOB_AGG_STATUS_URI = JOB_AGG_STATUS_URI; + const parameter = props.sendOrgState; + const getValue = parameter.key; + ORG_JOB_AGG_STATUS_URI = `${JOB_AGG_STATUS_URI}?org=${getValue}`; + + const chartProps = React.useMemo( + () => ({ + url: ORG_JOB_AGG_STATUS_URI, + accessorFnAggregation: (jobStatusesPerDay) => jobStatusesPerDay, + componentsFn: () => + Object.entries(JobStatusColors).map(([jobStatus, jobColor]) => ( + + )), + }), + [ORG_JOB_AGG_STATUS_URI], + ); + + return ; +}); + +export const JobTypeBarChart = React.memo((props) => { + console.debug("JobTypeBarChart rendered!"); + /* eslint-disable */ + var ORG_JOB_AGG_TYPE_URI = JOB_AGG_TYPE_URI; + const parameter = props.sendOrgState; + const getValue = parameter.key; + ORG_JOB_AGG_TYPE_URI = `${JOB_AGG_TYPE_URI}?org=${getValue}`; + + const chartProps = React.useMemo( + () => ({ + url: ORG_JOB_AGG_TYPE_URI, + accessorFnAggregation: (jobTypesPerDay) => jobTypesPerDay, + componentsFn: () => + Object.entries(JobTypeColors).map(([jobType, jobColor]) => ( + + )), + }), + [ORG_JOB_AGG_TYPE_URI], + ); + + return ; +}); + +export const JobObsClassificationBarChart = React.memo((props) => { + console.debug("JobObsClassificationBarChart rendered!"); + /* eslint-disable */ + var ORG_JOB_AGG_OBS_CLASSIFICATION_URI = JOB_AGG_OBS_CLASSIFICATION_URI; + const parameter = props.sendOrgState; + const getValue = parameter.key; + ORG_JOB_AGG_OBS_CLASSIFICATION_URI = `${JOB_AGG_OBS_CLASSIFICATION_URI}?org=${getValue}`; + + const chartProps = React.useMemo( + () => ({ + url: ORG_JOB_AGG_OBS_CLASSIFICATION_URI, + accessorFnAggregation: (jobObservableSubTypesPerDay) => + jobObservableSubTypesPerDay, + componentsFn: () => + Object.entries(ObservableClassificationColors).map( + ([observableClassification, observableColor]) => ( + + ), + ), + }), + [ORG_JOB_AGG_OBS_CLASSIFICATION_URI], + ); + + return ; +}); + +export const JobFileMimetypeBarChart = React.memo((props) => { + console.debug("JobFileMimetypeBarChart rendered!"); + /* eslint-disable */ + var ORG_JOB_AGG_FILE_MIMETYPE_URI = JOB_AGG_FILE_MIMETYPE_URI; + const parameter = props.sendOrgState; + const getValue = parameter.key; + ORG_JOB_AGG_FILE_MIMETYPE_URI = `${JOB_AGG_FILE_MIMETYPE_URI}?org=${getValue}`; + + const chartProps = React.useMemo( + () => ({ + url: ORG_JOB_AGG_FILE_MIMETYPE_URI, + accessorFnAggregation: (jobFileSubTypesPerDay) => + jobFileSubTypesPerDay?.aggregation, + componentsFn: (respData) => { + const { values: mimetypeList } = respData; + if (!mimetypeList || !mimetypeList?.length) return null; + return mimetypeList.map((mimetype, index) => ( + + )); + }, + }), + [ORG_JOB_AGG_FILE_MIMETYPE_URI], + ); + + return ; +}); + +// pie charts + +export const JobObsNamePieChart = React.memo((props) => { + console.debug("JobObsNamePieChart rendered!"); + /* eslint-disable */ + var ORG_JOB_AGG_OBS_NAME_URI = JOB_AGG_OBS_NAME_URI; + const parameter = props.sendOrgState; + const getValue = parameter.key; + ORG_JOB_AGG_OBS_NAME_URI = `${JOB_AGG_OBS_NAME_URI}?org=${getValue}`; + + const chartProps = React.useMemo( + () => ({ + url: ORG_JOB_AGG_OBS_NAME_URI, + modifierFn: (respData) => + Object.entries(respData?.aggregation).map( + ([observableName, analyzedTimes], index) => ({ + name: observableName.toLowerCase(), + value: analyzedTimes, + fill: colors[index], + }), + ), + }), + [ORG_JOB_AGG_OBS_NAME_URI], + ); + + return ; +}); + +export const JobFileHashPieChart = React.memo((props) => { + console.debug("JobFileHashPieChart rendered!"); + /* eslint-disable */ + var ORG_JOB_AGG_FILE_MD5_URI = JOB_AGG_FILE_MD5_URI; + const parameter = props.sendOrgState; + const getValue = parameter.key; + ORG_JOB_AGG_FILE_MD5_URI = `${JOB_AGG_FILE_MD5_URI}?org=${getValue}`; + + const chartProps = React.useMemo( + () => ({ + url: ORG_JOB_AGG_FILE_MD5_URI, + modifierFn: (respData) => + Object.entries(respData?.aggregation).map( + ([fileMd5, analyzedTimes], index) => ({ + name: fileMd5.toLowerCase(), + value: analyzedTimes, + fill: colors[index], + }), + ), + }), + [ORG_JOB_AGG_FILE_MD5_URI], + ); + + return ; +}); diff --git a/Submodules/IntelOwl/frontend/src/components/home/Home.jsx b/Submodules/IntelOwl/frontend/src/components/home/Home.jsx new file mode 100644 index 0000000..9694e29 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/home/Home.jsx @@ -0,0 +1,90 @@ +import "./Home.scss"; + +import React from "react"; +import { Container } from "reactstrap"; + +import { ContentSection } from "@certego/certego-ui"; + +import { PUBLIC_URL, VERSION } from "../../constants/environment"; + +// constants +const versionText = VERSION; +const logoBgImg = `url('${PUBLIC_URL}/logo-negative.png')`; +const blogPosts = [ + { + title: "IntelOwl: Release v4.0.0", + subText: "Certego Blog: v4.0.0 Announcement", + date: "1st July 2022", + link: "https://www.certego.net/en/news/intel-owl-release-v4-0-0/", + }, + { + title: "IntelOwl: Release v3.0.0", + subText: "Honeynet Blog: v3.0.0 Announcement", + date: "13th September 2021", + link: "https://www.honeynet.org/2021/09/13/intel-owl-release-v3-0-0/", + }, + { + title: + "Intel Owl – OSINT tool automates the intel-gathering process using a single API", + subText: "Daily Swig: Interview with Matteo Lodi and Eshaan Bansal", + date: "18th August 2020", + link: "https://portswigger.net/daily-swig/intel-owl-osint-tool-automates-the-intel-gathering-process-using-a-single-api", + }, + { + title: "New year, new tool: Intel Owl", + subText: "Certego Blog: First announcement", + date: "2nd January 2020", + link: "https://www.certego.net/en/news/new-year-new-tool-intel-owl/", + }, +]; + +// Component +export default function Home() { + console.debug("Home rendered!"); + + return ( + <> + {/* BG Image */} + +

+ {versionText} +

+
+ {/* Content */} + + + Intel Owl is an Open Source Intelligence, or OSINT solution to get + threat intelligence data about a specific file, an IP or a domain from + a single API at scale. It integrates a number of analyzers available + online and a lot of cutting-edge malware analysis tools. It is for + everyone who needs a single point to query for info about a specific + file or observable. + +
+ {/* blogposts */} +
IntelOwl News
+ + {blogPosts.map(({ title, subText, date, link }) => ( + + {date} +
{title}
+

{subText}

+
+ Read + +
+ ))} + +
+ + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/home/Home.scss b/Submodules/IntelOwl/frontend/src/components/home/Home.scss new file mode 100644 index 0000000..0847fdb --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/home/Home.scss @@ -0,0 +1,34 @@ +// Home.jsx + +@import url("https://fonts.googleapis.com/css?family=Pacifico:400"); + +html { + overflow-x: hidden; +} + +#home__bgImg { + background-color: #000; + position: relative; + height: 20vh; + width: 100vw; + top: -25px; + left: -50px; + opacity: 1; + background-position: center; + background-size: 35%; + background-repeat: no-repeat; + min-height: 100% !important; +} + +#home__versionText { + position: absolute; + top: 10%; + left: 70%; + font-family: Pacifico; + transform: rotate(335deg) !important; + -ms-transform: rotate(335deg) !important; +} + +#guidebox { + border-radius: 10px; +} diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/flow/CustomInvestigationNode.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/flow/CustomInvestigationNode.jsx new file mode 100644 index 0000000..10e769b --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/flow/CustomInvestigationNode.jsx @@ -0,0 +1,64 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { NodeToolbar, Handle, Position } from "reactflow"; +import "reactflow/dist/style.css"; +import { Button, UncontrolledTooltip } from "reactstrap"; +import { FaSearchPlus } from "react-icons/fa"; +import { AddExistingJobPopover } from "./investigationActions"; + +function CustomInvestigationNode({ data }) { + return ( + <> + +
+
+ + + Scan a new observable or a file to add to this investigation + +
+ +
+
+
+ {data?.label} +
+ + + ); +} + +CustomInvestigationNode.propTypes = { + data: PropTypes.object.isRequired, +}; + +export default React.memo(CustomInvestigationNode); diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/flow/CustomJobNode.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/flow/CustomJobNode.jsx new file mode 100644 index 0000000..098d72e --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/flow/CustomJobNode.jsx @@ -0,0 +1,137 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { NodeToolbar, Handle, Position } from "reactflow"; +import "reactflow/dist/style.css"; +import { Button, UncontrolledTooltip } from "reactstrap"; +import { AiOutlineLink } from "react-icons/ai"; +import { LuGitBranchPlus } from "react-icons/lu"; +import { MdContentCopy } from "react-icons/md"; + +import { CopyToClipboardButton, DateHoverable } from "@certego/certego-ui"; + +import { RemoveJob } from "./investigationActions"; + +function CustomJobNode({ data }) { + return ( + <> + {/* Number of children */} + +
{data.children.length} items
+
+ {/* Actions */} + +
+ + Copy + + + + Go to job #{data.id} result page + + + + Analyze the same observable again + + {data.isFirstLevel && } +
+
+
+ Job: + #{data.id} +
+
+ Name: + {data?.name} +
+
+ Playbook: + + {data?.playbook || "Custom analysis"} + +
+
+ Created: + + + +
+
+
+
+ {data?.name} +
+ + + + ); +} + +CustomJobNode.propTypes = { + data: PropTypes.object.isRequired, +}; + +export default React.memo(CustomJobNode); diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/flow/InvestigationFlow.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/flow/InvestigationFlow.jsx new file mode 100644 index 0000000..0045193 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/flow/InvestigationFlow.jsx @@ -0,0 +1,117 @@ +/* eslint-disable id-length */ +import React from "react"; +import PropTypes from "prop-types"; +import ReactFlow, { + Controls, + MiniMap, + MarkerType, + useNodesState, + useEdgesState, + Panel, +} from "reactflow"; +import "reactflow/dist/style.css"; + +import CustomInvestigationNode from "./CustomInvestigationNode"; +import CustomJobNode from "./CustomJobNode"; +import { getNodesAndEdges } from "./utils"; + +// Important! This must be defined outside of the component +const nodeTypes = { + investigationNode: CustomInvestigationNode, + jobNode: CustomJobNode, +}; + +const defaultEdgeOptions = { + style: { strokeWidth: 2 }, + type: "step", + markerEnd: { + type: MarkerType.ArrowClosed, + }, +}; + +export function InvestigationFlow(props) { + console.debug("InvestigationFlow rendered"); + const { + investigationTree, + investigationId, + refetchTree, + refetchInvestigation, + ...rest + } = props; + + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + + React.useEffect(() => { + const [initialNodes, initialEdges] = getNodesAndEdges( + investigationTree, + investigationId, + refetchTree, + refetchInvestigation, + ); + setNodes(initialNodes); + setEdges(initialEdges); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [investigationTree]); + + return ( +
+ + + + +
+ Edges: +
+
+ job is concluded +
+
+
+ job is running +
+
+
+
+
+ ); +} + +InvestigationFlow.propTypes = { + investigationId: PropTypes.number.isRequired, + investigationTree: PropTypes.object.isRequired, + refetchTree: PropTypes.func.isRequired, + refetchInvestigation: PropTypes.func.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/flow/investigationActions.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/flow/investigationActions.jsx new file mode 100644 index 0000000..9155396 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/flow/investigationActions.jsx @@ -0,0 +1,101 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { + Button, + Input, + UncontrolledPopover, + UncontrolledTooltip, +} from "reactstrap"; +import { MdOutlineCancel } from "react-icons/md"; +import { BsFillPlusCircleFill } from "react-icons/bs"; + +import { removeJob, addExistingJob } from "../result/investigationApi"; + +export function AddExistingJobPopover({ data }) { + // state + const [jobToAdd, setJobToAdd] = React.useState(null); + + const onClick = async () => { + const success = await addExistingJob(jobToAdd, data.id); + if (success) { + data.refetchInvestigation(); + data.refetchTree(); + } + setJobToAdd(null); + }; + + return ( +
+ + +
+ setJobToAdd(event.target.value)} + placeholder="Enter a job id" + style={{ maxHeight: "40px", maxWidth: "60vh" }} + className="bg-dark" + /> + +
+
+
+ ); +} + +AddExistingJobPopover.propTypes = { + data: PropTypes.object.isRequired, +}; + +export function RemoveJob({ data }) { + const onClick = async () => { + const success = await removeJob(data.investigation, data.id); + if (success) { + data.refetchInvestigation(); + data.refetchTree(); + } + }; + + return ( + <> + + + Remove job #{data.id} and all its children from the investigation + + + ); +} + +RemoveJob.propTypes = { + data: PropTypes.object.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/flow/utils.js b/Submodules/IntelOwl/frontend/src/components/investigations/flow/utils.js new file mode 100644 index 0000000..13c537b --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/flow/utils.js @@ -0,0 +1,141 @@ +import dagre from "@dagrejs/dagre"; +import { JobFinalStatuses } from "../../../constants/jobConst"; + +/* eslint-disable id-length */ +function addJobNode( + nodes, + job, + investigationId, + refetchTree, + refetchInvestigation, + isFirstLevel, +) { + nodes.push({ + id: `job-${job.pk}`, + data: { + id: job.pk, + label: `job #${job.pk}`, + name: job.analyzed_object_name, + playbook: job.playbook, + investigation: investigationId, + children: job.children || [], + status: job.status, + refetchTree, + refetchInvestigation, + isFirstLevel: isFirstLevel || false, + created: job.received_request_time, + }, + type: "jobNode", + }); + + // recursive call if there are children + if (job.children) { + job.children.forEach((child) => { + addJobNode(nodes, child, investigationId, refetchTree); + }); + } +} + +function addEdge(edges, job, parentType, parentId) { + edges.push({ + id: `edge-${parentType}${parentId}-job${job.pk}`, + source: `${parentType}-${parentId}`, + target: `job-${job.pk}`, + animated: !Object.values(JobFinalStatuses).includes(job.status), + }); + + // recursive call if there are children + if (job.children) { + job.children.forEach((child) => { + addEdge(edges, child, "job", job.pk); + }); + } +} + +function getLayoutedElements(nodes, edges) { + // needed for graph layout + const dagreGraph = new dagre.graphlib.Graph(); + dagreGraph.setDefaultEdgeLabel(() => ({})); + + const nodeWidth = 300; + const nodeHeight = 60; + + dagreGraph.setGraph({ rankdir: "LR" }); + + nodes.forEach((node) => { + dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight }); + }); + + edges.forEach((edge) => { + dagreGraph.setEdge(edge.source, edge.target); + }); + + dagre.layout(dagreGraph); + + nodes.forEach((node) => { + const nodeWithPosition = dagreGraph.node(node.id); + // eslint-disable-next-line no-param-reassign + node.position = { + x: nodeWithPosition.x - nodeWidth / 2 + 150, + y: nodeWithPosition.y - nodeHeight / 2 + 70, + }; + return node; + }); + return { nodes, edges }; +} + +export function getNodesAndEdges( + investigationTree, + investigationId, + refetchTree, + refetchInvestigation, +) { + // investigation node + const initialNode = [ + { + id: `investigation-${investigationId}`, + position: { x: 2, y: 2 }, + data: { + id: investigationId, + label: investigationTree.name, + refetchTree, + refetchInvestigation, + }, + type: "investigationNode", + draggable: false, + }, + ]; + // jobs nodes + const jobsNodes = []; + + // edges + const initialEdges = []; + const jobsEdges = []; + + if (investigationTree.jobs.length) { + investigationTree.jobs.forEach((job) => { + addJobNode( + jobsNodes, + job, + investigationId, + refetchTree, + refetchInvestigation, + true, + ); + addEdge(jobsEdges, job, "investigation", investigationId); + }); + } + + if (jobsEdges.length) { + const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements( + jobsNodes, + jobsEdges, + ); + return [ + initialNode.concat(layoutedNodes), + initialEdges.concat(layoutedEdges), + ]; + } + + return [initialNode, initialEdges]; +} diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationActionBar.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationActionBar.jsx new file mode 100644 index 0000000..174e5f7 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationActionBar.jsx @@ -0,0 +1,45 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { useNavigate } from "react-router-dom"; + +import { ContentSection, IconButton, addToast } from "@certego/certego-ui"; + +import { deleteInvestigation } from "./investigationApi"; +import { DeleteIcon } from "../../common/icon/icons"; +import { areYouSureConfirmDialog } from "../../common/areYouSureConfirmDialog"; + +export function InvestigationActionsBar({ investigation }) { + // routers + const navigate = useNavigate(); + + // callbacks + const onDeleteBtnClick = async () => { + const sure = await areYouSureConfirmDialog( + `delete investigation #${investigation.id}`, + ); + if (!sure) return null; + const success = await deleteInvestigation(investigation.id); + if (!success) return null; + addToast("Redirecting...", null, "secondary"); + setTimeout(() => navigate(-1), 250); + return null; + }; + + return ( + + + + ); +} + +InvestigationActionsBar.propTypes = { + investigation: PropTypes.object.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationInfoCard.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationInfoCard.jsx new file mode 100644 index 0000000..5e9e1d1 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationInfoCard.jsx @@ -0,0 +1,157 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { + Button, + ListGroup, + ListGroupItem, + Collapse, + Row, + Col, + UncontrolledTooltip, + Input, +} from "reactstrap"; +import { MdEdit } from "react-icons/md"; +import { BsFillCheckSquareFill } from "react-icons/bs"; + +import { + ContentSection, + DateHoverable, + ArrowToggleIcon, + IconButton, +} from "@certego/certego-ui"; + +import { JobTag } from "../../common/JobTag"; +import { StatusTag } from "../../common/StatusTag"; +import { TLPTag } from "../../common/TLPTag"; +import { updateInvestigation } from "./investigationApi"; + +export function InvestigationInfoCard({ investigation, refetchTree }) { + // local state + const [isOpen, setIsOpen] = React.useState(false); + + const [isEditing, setIsEditing] = React.useState(false); + const [investigationName, setInvestigationName] = React.useState( + investigation?.name, + ); + + const editInvestigationName = async () => { + if (investigation.name !== investigationName) { + const success = await updateInvestigation(investigation.id, { + name: investigationName, + }); + if (success) refetchTree(); + if (!success) return; + } + setIsEditing(false); + }; + + return ( +
+ + + + {isEditing ? ( + <> + { + setInvestigationName(event.target.value); + }} + value={investigationName} + style={{ + maxWidth: "600px", + maxHeight: "20px", + overflowX: "scroll", + }} + className="me-2 bg-dark" + /> + + + ) : ( + <> +

{investigationName}

+ setIsEditing(true)} + title="Edit name" + titlePlacement="top" + /> + + )} + + + + + Toggle Investigation Metadata + + +
+
+ + + + {[ + ["Status", ], + ["TLP", ], + [ + "Tags", + investigation.tags.map( + (tag) => + tag !== null && ( + + ), + ), + ], + ["User", investigation.owner], + [ + "Start Time", + , + ], + ].map(([jobFieldName, jobFieldValue]) => ( + + {jobFieldName} +
{jobFieldValue}
+
+ ))} +
+
+
+
+ ); +} + +InvestigationInfoCard.propTypes = { + investigation: PropTypes.object.isRequired, + refetchTree: PropTypes.func.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationOverview.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationOverview.jsx new file mode 100644 index 0000000..a3754d5 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationOverview.jsx @@ -0,0 +1,181 @@ +import React from "react"; +import PropTypes from "prop-types"; +import useAxios from "axios-hooks"; +import { Col, Row, Container, Input } from "reactstrap"; +import { MdEdit } from "react-icons/md"; +import { BsFillCheckSquareFill, BsMarkdown } from "react-icons/bs"; +import { useLocation } from "react-router-dom"; + +import { IconButton, Loader } from "@certego/certego-ui"; +import { StatusIcon } from "../../common/icon/StatusIcon"; + +import { InvestigationInfoCard } from "./InvestigationInfoCard"; +import { InvestigationActionsBar } from "./InvestigationActionBar"; +import { updateInvestigation } from "./investigationApi"; +import { InvestigationFlow } from "../flow/InvestigationFlow"; +import { INVESTIGATION_BASE_URI } from "../../../constants/apiURLs"; +import { markdownToHtml } from "../../common/markdownToHtml"; + +export function InvestigationOverview({ + isRunningInvestigation, + investigation, + refetchInvestigation, +}) { + console.debug("InvestigationOverview rendered"); + + // state + const location = useLocation(); + console.debug( + `location pathname: ${location.pathname}, state: ${JSON.stringify( + location?.state, + )}`, + ); + + // API to download investigation tree + const [{ data: investigationTree, loading, error }, refetchTree] = useAxios({ + url: `${INVESTIGATION_BASE_URI}/${investigation.id}/tree`, + }); + + // refetch tree after the investigation is complete + React.useEffect(() => { + if (!loading) refetchTree(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isRunningInvestigation]); + + const [isEditing, setIsEditing] = React.useState(false); + const [investigationDescription, setInvestigationDescription] = + React.useState(investigation?.description); + + // API to edit investigation description + const editInvestigationDescription = async () => { + if (investigation.description !== investigationDescription) { + const success = await updateInvestigation(investigation.id, { + description: investigationDescription, + }); + if (!success) return; + } + setIsEditing(false); + }; + + return ( + + {/* bar with investigation id and utilities buttons */} + + +

+ + Investigation #{investigation.id} + + +

+ + + + +
+ {/* investigation metadata card */} + + + + + + +
+ Description + setIsEditing(true)} + title="Edit description" + titlePlacement="top" + /> + + {isEditing && ( + + )} +
+
+ + {isEditing ? ( + { + setInvestigationDescription(event.target.value); + }} + placeholder="Enter a description" + value={investigationDescription} + style={{ minHeight: "200px", overflowY: "auto" }} + className="bg-dark" + /> + ) : ( +
+ {investigationDescription + ? markdownToHtml(investigationDescription) + : "No description"} +
+ )} +
+ + ( + + )} + /> + +
+ ); +} + +InvestigationOverview.propTypes = { + isRunningInvestigation: PropTypes.bool.isRequired, + investigation: PropTypes.object.isRequired, + refetchInvestigation: PropTypes.func.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationResult.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationResult.jsx new file mode 100644 index 0000000..b0fb377 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/result/InvestigationResult.jsx @@ -0,0 +1,75 @@ +import React from "react"; +import useAxios from "axios-hooks"; +import useTitle from "react-use/lib/useTitle"; +import useInterval from "react-use/lib/useInterval"; +import { useParams } from "react-router-dom"; + +import { Loader } from "@certego/certego-ui"; +import { INVESTIGATION_BASE_URI } from "../../../constants/apiURLs"; +import { InvestigationOverview } from "./InvestigationOverview"; + +export default function InvestigationResult() { + console.debug("InvestigationResult rendered!"); + + // local state + const [initialLoading, setInitialLoading] = React.useState(true); + const [isRunning, setIsRunning] = React.useState(false); + + // from props + const params = useParams(); + const investigationId = params.id; + + // API to download the investigation data + const [{ data: investigation, loading, error }, refetchInvestigation] = + useAxios({ + url: `${INVESTIGATION_BASE_URI}/${investigationId}`, + }); + + // in case the investigation is not running and started (the investigation is not undefined) it means it terminated. + const investigationConcluded = investigation !== undefined && !isRunning; + + console.debug( + `InvestigationResult - initialLoading: ${initialLoading}, isRunning: ${isRunning}, ` + + ` investigationConcluded: ${investigationConcluded}`, + ); + + // HTTP polling only in case the investigation is running + useInterval( + refetchInvestigation, + isRunning ? 5 * 1000 : null, // 5 seconds + ); + + // every time the investigation data are downloaded we check if it terminated or not + React.useEffect( + () => + setIsRunning( + investigation === undefined || + ["running"].includes(investigation.status), + ), + [investigation], + ); + + // initial loading (spinner) + React.useEffect(() => { + if (!loading) setInitialLoading(false); + }, [loading]); + + // page title + useTitle(`IntelOwl | Investigation (#${investigationId})`, { + restoreOnUnmount: true, + }); + + return ( + ( + + )} + /> + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/result/investigationApi.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/result/investigationApi.jsx new file mode 100644 index 0000000..12815c3 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/result/investigationApi.jsx @@ -0,0 +1,193 @@ +import React from "react"; +import axios from "axios"; + +import { addToast } from "@certego/certego-ui"; + +import { + INVESTIGATION_BASE_URI, + JOB_BASE_URI, +} from "../../../constants/apiURLs"; +import { areYouSureConfirmDialog } from "../../common/areYouSureConfirmDialog"; +import { prettifyErrors } from "../../../utils/api"; + +export async function createInvestigation() { + let success = false; + const data = { + name: "Custom investigation", + description: "", + for_organization: true, + }; + try { + const response = await axios.post(`${INVESTIGATION_BASE_URI}`, data); + success = response.status === 201; + if (success) { + addToast( + Created Investigation #{response.data.id}, + null, + "success", + ); + return response.data.id; + } + } catch (error) { + addToast( + "Failed to create new investigation", + prettifyErrors(error), + "warning", + ); + } + return success; +} + +export async function deleteInvestigation(investigationId) { + let success = false; + try { + const response = await axios.delete( + `${INVESTIGATION_BASE_URI}/${investigationId}`, + ); + success = response.status === 204; + if (success) { + addToast( + Deleted Investigation #{investigationId}, + null, + "info", + ); + } + } catch (error) { + addToast( + `Failed to delete investigation #${investigationId}`, + prettifyErrors(error), + "warning", + ); + } + return success; +} + +export async function updateInvestigation(investigationId, data) { + let success = false; + try { + const response = await axios.patch( + `${INVESTIGATION_BASE_URI}/${investigationId}`, + data, + ); + success = response.status === 200; + if (success) { + addToast( + Updated Investigation #{investigationId}, + null, + "info", + ); + } + } catch (error) { + addToast( + `Failed to update investigation #${investigationId}`, + prettifyErrors(error), + "warning", + ); + } + return success; +} + +export async function addJob(investigationId, jobId) { + let success = false; + const data = { job: jobId }; + try { + const response = await axios.post( + `${INVESTIGATION_BASE_URI}/${investigationId}/add_job`, + data, + ); + success = response.status === 200; + if (success) { + addToast( + + Job #{jobId} added to the Investigation #{investigationId} + , + null, + "success", + ); + } + } catch (error) { + addToast( + `Failed to add job #${jobId} to the investigation #${investigationId}`, + prettifyErrors(error), + "warning", + ); + } + return success; +} + +export async function removeJob(investigationId, jobId) { + let success = false; + try { + const response = await axios.post( + `${INVESTIGATION_BASE_URI}/${investigationId}/remove_job`, + { job: jobId }, + ); + success = response.status === 200; + if (success) { + addToast( + + Job #{jobId} removed from the Investigation #{investigationId} + , + null, + "success", + ); + } + } catch (error) { + addToast( + `Failed to remove job #${jobId} from the investigation #${investigationId}`, + prettifyErrors(error), + "warning", + ); + } + return success; +} + +export async function addExistingJob(jobToAdd, currentInvestigationId) { + let success = false; + let jobInvestigationId = null; + try { + const response = await axios.get(`${JOB_BASE_URI}/${jobToAdd}`); + success = response.status === 200; + if (success) { + jobInvestigationId = response.data.investigation; + } + } catch (error) { + addToast( + `Failed to add job #${jobToAdd} to the investigation #${currentInvestigationId}`, + prettifyErrors(error), + "warning", + ); + return success; + } + + // case 1 - Job is already part of this investigation + if (jobInvestigationId === currentInvestigationId) { + addToast( + `Failed to add job #${jobToAdd} to the investigation #${currentInvestigationId}`, + "Job is already part of this investigation", + "warning", + ); + } + // case 2 - job is already part of different investigation + else if (jobInvestigationId) { + const sure = await areYouSureConfirmDialog( + `Remove job #${jobToAdd} from investigation #${jobInvestigationId} and add into investigation #${currentInvestigationId}`, + ); + if (sure) { + // remove job from previous investigation + const isJobRemoved = await removeJob(jobInvestigationId, jobToAdd); + if (isJobRemoved) { + // add job into current investigation + const isJobAdded = await addJob(currentInvestigationId, jobToAdd); + return isJobAdded; + } + return isJobRemoved; + } + } + // case 3 - job is not part of any investigation + else { + const isJobAdded = await addJob(currentInvestigationId, jobToAdd); + return isJobAdded; + } + return false; +} diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/table/InvestigationsTable.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/table/InvestigationsTable.jsx new file mode 100644 index 0000000..e18c214 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/table/InvestigationsTable.jsx @@ -0,0 +1,110 @@ +/* eslint-disable react/prop-types */ +import React from "react"; +import { Container, Row, Col, UncontrolledTooltip } from "reactstrap"; +import { MdInfoOutline } from "react-icons/md"; + +import { + ElasticTimePicker, + SyncButton, + TableHintIcon, + useDataTable, + useTimePickerStore, +} from "@certego/certego-ui"; + +import useTitle from "react-use/lib/useTitle"; + +import { INVESTIGATION_BASE_URI } from "../../../constants/apiURLs"; +import { investigationTableColumns } from "./investigationTableColumns"; + +// constants +const toPassTableProps = { + columns: investigationTableColumns, + tableEmptyNode: ( + <> +

No Data

+ Note: Try changing time filter. + + ), +}; + +// component +export default function InvestigationsTable() { + console.debug("InvestigationsTable rendered!"); + + // page title + useTitle("IntelOwl | Investigation History", { restoreOnUnmount: true }); + + // consume zustand store + const { range, fromTimeIsoStr, onTimeIntervalChange } = useTimePickerStore(); + + // state + const [initialLoading, setInitialLoading] = React.useState(true); + + // API/ Table + const [data, tableNode, refetch, _, loadingTable] = useDataTable( + { + url: INVESTIGATION_BASE_URI, + params: { + start_time__gte: fromTimeIsoStr, + }, + initialParams: { + ordering: "-start_time", + }, + }, + toPassTableProps, + ); + + React.useEffect(() => { + if (!loadingTable) setInitialLoading(false); + }, [loadingTable]); + + React.useEffect(() => { + if (!initialLoading) refetch(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [initialLoading]); + + return ( + + {/* Basic */} + + +

+ Investigations History  + {data?.count} total +

+
+ + + Investigations are a framework to connect jobs with each other, + correlate the findings and collaborate with teammates to reach + common goals. + +
+ + + + +
+ {/* Actions */} +
+ + +
+
+ {/* Table */} + {tableNode} +
+
+ ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/investigations/table/investigationTableColumns.jsx b/Submodules/IntelOwl/frontend/src/components/investigations/table/investigationTableColumns.jsx new file mode 100644 index 0000000..e82a539 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/investigations/table/investigationTableColumns.jsx @@ -0,0 +1,131 @@ +/* eslint-disable react/prop-types */ +import React from "react"; + +import { + DefaultColumnFilter, + SelectOptionsFilter, + LinkOpenViewIcon, + DateHoverable, + CopyToClipboardButton, +} from "@certego/certego-ui"; + +import { JobTag } from "../../common/JobTag"; +import { StatusTag } from "../../common/StatusTag"; +import { TLPTag } from "../../common/TLPTag"; +import { TlpChoices } from "../../../constants/advancedSettingsConst"; +import { InvestigationStatuses } from "../../../constants/investigationConst"; + +export const investigationTableColumns = [ + { + Header: () => "ID", // No header + id: "id", + accessor: "id", + maxWidth: 75, + disableSortBy: true, + Cell: ({ value: id }) => ( +
+

#{id}

+ +
+ ), + Filter: DefaultColumnFilter, + }, + { + Header: "Created", + id: "start_time", + accessor: "start_time", + Cell: ({ value }) => ( + + ), + maxWidth: 100, + }, + { + Header: "User", + id: "owner", + accessor: "owner", + Cell: ({ value, row: { original: investigation } }) => ( + + {value} + + ), + disableSortBy: true, + Filter: DefaultColumnFilter, + maxWidth: 120, + }, + { + Header: "Name", + id: "name", + accessor: "name", + Cell: ({ value, row: { original: investigation } }) => ( + + {value} + + ), + disableSortBy: true, + Filter: DefaultColumnFilter, + }, + { + Header: "TLP", + id: "tlp", + accessor: "tlp", + Cell: ({ value }) => , + disableSortBy: true, + Filter: SelectOptionsFilter, + selectOptions: TlpChoices, + maxWidth: 90, + }, + { + Header: "Tags", + id: "tags", + accessor: "tags", + Cell: ({ value }) => + value.map( + (tag) => + tag !== null && ( + + ), + ), + disableSortBy: true, + maxWidth: 100, + Filter: DefaultColumnFilter, + filterValueAccessorFn: (tags) => tags.map((tag) => tag.label), + }, + { + Header: "Jobs created", + id: "total_jobs", + accessor: "total_jobs", + Cell: ({ value }) => value, + disableSortBy: true, + maxWidth: 90, + }, + { + Header: "Status", + id: "status", + accessor: "status", + Cell: ({ value }) => , + disableSortBy: true, + Filter: SelectOptionsFilter, + selectOptions: Object.values(InvestigationStatuses), + maxWidth: 110, + }, +]; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/notification/NotificationPopoverButton.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/notification/NotificationPopoverButton.jsx new file mode 100644 index 0000000..ff8d239 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/notification/NotificationPopoverButton.jsx @@ -0,0 +1,85 @@ +import React from "react"; +import { Badge, UncontrolledPopover } from "reactstrap"; +import { IoMdNotifications } from "react-icons/io"; + +import { IconButton, Tabs, useAxiosComponentLoader } from "@certego/certego-ui"; + +import { NOTIFICATION_BASE_URI } from "../../../constants/apiURLs"; +import NotificationsList from "./NotificationsList"; + +export default function NotificationPopoverButton() { + // API + const [unreadNotifs, Loader1, refetch1] = useAxiosComponentLoader( + { + url: NOTIFICATION_BASE_URI, + params: { page_size: 4, read: false }, + }, + (respData) => respData?.results, + ); + + const [readNotifs, Loader2, refetch2] = useAxiosComponentLoader( + { + url: NOTIFICATION_BASE_URI, + params: { page_size: 4, read: true }, + }, + (respData) => respData?.results, + ); + + const refetchFn = React.useCallback(async () => { + await refetch1(); + await refetch2(); + }, [refetch1, refetch2]); + + return ( + <> + + {unreadNotifs?.length > 0 && ( + + {unreadNotifs?.length} + + )} + + ( + ( + + )} + /> + ), + // Read + () => ( + ( + + )} + /> + ), + ]} + /> + + + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/notification/NotificationsList.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/notification/NotificationsList.jsx new file mode 100644 index 0000000..05030c6 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/notification/NotificationsList.jsx @@ -0,0 +1,73 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { + ListGroup, + ListGroupItem, + ListGroupItemHeading, + ListGroupItemText, +} from "reactstrap"; +import { IoCheckmarkDoneSharp } from "react-icons/io5"; + +import { ContentSection, IconButton, DateHoverable } from "@certego/certego-ui"; + +import { notificationMarkAsRead } from "./notificationApi"; + +export default function NotificationsList({ notifications, refetchFn }) { + const markAsReadCb = React.useCallback( + async (notifId) => { + try { + await notificationMarkAsRead(notifId); + await refetchFn(); + } catch (error) { + // handled inside notificationMarkAsRead + } + }, + [refetchFn], + ); + + return notifications.length > 0 ? ( + + {notifications.map((notif) => ( + +
+ + {notif?.title} + + + + +
+ +
+ {notif?.read === false && ( + markAsReadCb(notif.id)} + /> + )} +
+
+ ))} +
+ ) : ( +
No items
+ ); +} + +NotificationsList.propTypes = { + notifications: PropTypes.array.isRequired, + refetchFn: PropTypes.func.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/notification/notificationApi.js b/Submodules/IntelOwl/frontend/src/components/jobs/notification/notificationApi.js new file mode 100644 index 0000000..e5faac9 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/notification/notificationApi.js @@ -0,0 +1,15 @@ +import axios from "axios"; + +import { addToast } from "@certego/certego-ui"; + +import { NOTIFICATION_BASE_URI } from "../../../constants/apiURLs"; + +export async function notificationMarkAsRead(notifId) { + try { + await axios.post(`${NOTIFICATION_BASE_URI}/${notifId}/mark-as-read`); + return Promise.resolve(true); + } catch (error) { + addToast("Failed!", error.parsedMsg, "danger", true); + return Promise.reject(error); + } +} diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/notifications.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/notifications.jsx new file mode 100644 index 0000000..f2bc15d --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/notifications.jsx @@ -0,0 +1,52 @@ +import { PUBLIC_URL } from "../../constants/environment"; +import { JobResultSections } from "../../constants/miscConst"; + +// This function is used to generate a notification when a Job is terminated +export function generateJobNotification(observableName, jobId) { + console.debug( + `send notification for observable: ${observableName}(${jobId})`, + ); + Notification.requestPermission().then((result) => { + if (result === "granted") { + // notification audio + new Audio(`${PUBLIC_URL}/notification.mp3`) + .play() + .then() + .catch((error) => console.error(error)); + + // notification icon + setNotificationFavicon(true); + + const notification = new Notification("IntelOwl analysis terminated!", { + body: `Observable: ${observableName} (job ${jobId}) reported.\n Click here to view the result!`, + icon: `${PUBLIC_URL}/logo-blue.png`, + }); + + // close the notification after 5 seconds + setTimeout(() => { + notification.close(); + }, 10 * 1000); + + // navigate to the Job report page when clicked + notification.addEventListener("click", () => { + window.open( + `/jobs/${jobId}/${JobResultSections.VISUALIZER}`, + '_blank,noopener,noreferrer"', + ); + }); + } else { + // eslint-disable-next-line no-console + console.warn( + "Without the permission for the notifications IntelOwl cannot report when Jobs terminated", + ); + } + }); +} + +// This function allow to toggle favicon and notification favicon +export function setNotificationFavicon(isNotification) { + const actualIcon = document.head.querySelector('link[rel="shortcut icon"]'); + actualIcon.href = isNotification + ? `${PUBLIC_URL}/icons/favicon-notification.ico` + : `${PUBLIC_URL}/icons/favicon.ico`; +} diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/JobInfoCard.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobInfoCard.jsx new file mode 100644 index 0000000..b98476e --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobInfoCard.jsx @@ -0,0 +1,251 @@ +import React from "react"; +import { useNavigate } from "react-router-dom"; +import PropTypes from "prop-types"; +import { + Button, + ListGroup, + ListGroupItem, + Badge, + Collapse, + Row, + Col, + UncontrolledTooltip, +} from "reactstrap"; + +import { + ContentSection, + DateHoverable, + CopyToClipboardButton, + ArrowToggleIcon, +} from "@certego/certego-ui"; +import { processTimeMMSS } from "../../../utils/time"; + +import { JobTag } from "../../common/JobTag"; +import { PlaybookTag } from "../../common/PlaybookTag"; +import { StatusTag } from "../../common/StatusTag"; +import { TLPTag } from "../../common/TLPTag"; +import { JobInfoIcon } from "./JobInfoIcon"; +import { JobIsRunningAlert } from "./JobIsRunningAlert"; +import { JobFinalStatuses } from "../../../constants/jobConst"; + +export function JobInfoCard({ job }) { + const navigate = useNavigate(); + // local state + const [isOpenJobInfoCard, setIsOpenJobInfoCard] = React.useState(false); + const [isOpenJobWarnings, setIsOpenJobWarnings] = React.useState(false); + const [isOpenJobErrors, setIsOpenJobErrors] = React.useState(false); + + return ( +
+ + + + {job.investigation && ( + <> + + + This job is part of the investigation #{job.investigation} + + + )} + + +

+ + {job.is_sample ? ( + + {job.file_name} + + ) : ( + + {job.observable_name} + + )} +

+ + {job.is_sample + ? `file: ${job.file_mimetype}` + : job.observable_classification} + + + + + + Toggle Job Metadata + + +
+
+ + + + {[ + ["Status", ], + ["TLP", ], + ["User", job.user?.username], + ["MD5", job.md5], + ["Process Time (mm:ss)", processTimeMMSS(job.process_time)], + [ + "Start Time", + , + ], + [ + "End Time", + job.finished_analysis_time ? ( + + ) : ( + "-" + ), + ], + ].map(([key, value]) => ( + + {key} +
{value}
+
+ ))} +
+ + {[ + [ + "Playbook", + , + ], + [ + "Tags", + job.tags.length ? ( + job.tags.map((tag) => ( + + )) + ) : ( + None + ), + ], + [ + "Warning(s)", + <> +
+ {job.warnings.length} warnings + + + Toggle Job Warnings + +
+ +
    + {job.warnings.map((error) => ( +
  • {error}
  • + ))} +
+
+ , + ], + [ + "Error(s)", + <> +
+ {job.errors.length} errors + + + Toggle Job Errors + +
+ +
    + {job.errors.map((error) => ( +
  • {error}
  • + ))} +
+
+ , + ], + ].map(([key, value]) => ( + + {key} +
{value}
+
+ ))} +
+ {Object.values(JobFinalStatuses).includes(job.status) && ( +
+ +
+ )} +
+
+
+ ); +} + +JobInfoCard.propTypes = { + job: PropTypes.object.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/JobInfoIcon.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobInfoIcon.jsx new file mode 100644 index 0000000..ff0fb80 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobInfoIcon.jsx @@ -0,0 +1,41 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { UncontrolledTooltip } from "reactstrap"; +import { VscGlobe, VscFile } from "react-icons/vsc"; + +import { extractCountry } from "./utils/extractCountry"; +import { getIcon } from "./visualizer/icons"; +import { ObservableClassifications } from "../../../constants/jobConst"; + +export function JobInfoIcon({ job }) { + let iconElement; + const country = extractCountry(job); + const countryIcon = getIcon(country.countryCode); + + // file + if (job.is_sample) iconElement = ; + // ip with country flag + else if ( + job.observable_classification === ObservableClassifications.IP && + country.countryCode + ) { + iconElement = ( + + {countryIcon} + + {country.countryName} + + + ); + } else { + iconElement = ; + } + return iconElement; +} + +JobInfoIcon.propTypes = { + job: PropTypes.object.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/JobIsRunningAlert.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobIsRunningAlert.jsx new file mode 100644 index 0000000..b756d56 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobIsRunningAlert.jsx @@ -0,0 +1,49 @@ +/* eslint-disable id-length */ +import React from "react"; +import PropTypes from "prop-types"; +import { ReactFlowProvider } from "reactflow"; +import "reactflow/dist/style.css"; +import { IconButton } from "@certego/certego-ui"; + +import { JobFinalStatuses } from "../../../constants/jobConst"; +import { areYouSureConfirmDialog } from "../../common/areYouSureConfirmDialog"; + +import { killJob } from "./jobApi"; +import { killJobIcon } from "../../common/icon/icons"; +import { JobIsRunningFlow } from "./flow/JobIsRunningFlow"; + +export function JobIsRunningAlert({ job }) { + const onKillJobBtnClick = async () => { + const sure = await areYouSureConfirmDialog(`Kill Job #${job.id}`); + if (!sure) return null; + await killJob(job.id); + return null; + }; + + return ( + <> + + + +
+ {job.permissions?.kill && + !Object.values(JobFinalStatuses).includes(job.status) && ( + + )} +
+ + ); +} + +JobIsRunningAlert.propTypes = { + job: PropTypes.object.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/JobOverview.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobOverview.jsx new file mode 100644 index 0000000..bc90d12 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobOverview.jsx @@ -0,0 +1,483 @@ +import React, { useEffect, useState } from "react"; +import PropTypes from "prop-types"; +import { + ButtonGroup, + Button, + Badge, + Col, + Row, + Nav, + NavItem, + NavLink, + Container, + TabContent, + TabPane, + Spinner, +} from "reactstrap"; + +import { Loader } from "@certego/certego-ui"; +import { JSONTree } from "react-json-tree"; + +import { useNavigate, useLocation } from "react-router-dom"; +import { PluginsReportTable } from "./pluginReportTables"; +import { + reportedPluginNumber, + reportedVisualizerNumber, + ReportedPluginTooltip, +} from "./utils/reportedPlugins"; +import { StatusIcon } from "../../common/icon/StatusIcon"; +import VisualizerReport from "./visualizer/visualizer"; +import { JobFinalStatuses } from "../../../constants/jobConst"; +import { PluginStatuses } from "../../../constants/pluginConst"; +import { JobResultSections } from "../../../constants/miscConst"; + +import { JobInfoCard } from "./JobInfoCard"; +import { JobIsRunningAlert } from "./JobIsRunningAlert"; +import { JobActionsBar } from "./bar/JobActionBar"; +import { usePluginConfigurationStore } from "../../../stores/usePluginConfigurationStore"; + +/* THESE IDS CANNOT BE EMPTY! +We perform a redirect in case the user landed in the visualzier page without a visualizer, +this is case happens because we don't know the available visualizers before enter in the job page: +ex: when we start a job from start scan we cannot know the visualizer pages. +When we land in the job page without a visualizer selected we need to redirect the user to a valid visualizer, +the redirect is based on the url: in case the parmam miss it means the page is not selected and we need to redirect +in case we use empty param for this page we fall in an infinite redirect loop. +*/ +const LOADING_VISUALIZER_UI_ELEMENT_CODE = "loading"; +const NO_VISUALIZER_UI_ELEMENT_CODE = "no-visualizer"; + +export function JobOverview({ + isRunningJob, + job, + refetch, + section, + subSection, +}) { + console.debug("JobOverview rendered"); + console.debug(`section: ${section}, subSection: ${subSection}`); + + const isSelectedUI = section === JobResultSections.VISUALIZER; + + const [ + analyzersLoading, + connectorsLoading, + visualizersLoading, + pivotsLoading, + analyzers, + connectors, + visualizers, + pivots, + ] = usePluginConfigurationStore((state) => [ + state.analyzersLoading, + state.connectorsLoading, + state.visualizersLoading, + state.pivotsLoading, + state.analyzers, + state.connectors, + state.visualizers, + state.pivots, + ]); + + const rawElements = React.useMemo( + () => [ + { + name: "analyzer", + nav: ( +
+ Analyzers Report + + {reportedPluginNumber(job.analyzer_reports)} /  + {job.analyzers_to_execute.length} + + +
+ ), + report: ( + + ), + }, + { + name: "connector", + nav: ( +
+ Connectors Report + + {reportedPluginNumber(job.connector_reports)} /  + {job.connectors_to_execute.length} + + +
+ ), + report: ( + + ), + }, + { + name: "pivot", + nav: ( +
+ Pivots Report + + {reportedPluginNumber(job.pivot_reports)} /  + {job.pivots_to_execute.length} + + +
+ ), + report: ( + + ), + }, + { + name: "visualizer", + nav: ( +
+ Visualizers Report + + {reportedVisualizerNumber( + job.visualizer_reports, + job.visualizers_to_execute, + )}{" "} + /  + {job.visualizers_to_execute.length} + + +
+ ), + report: ( + + ), + }, + { + name: "full", + nav: ( +
+ Full Report +
+ ), + report: ( +
+ true} + /> +
+ ), + }, + ], + // eslint-disable-next-line react-hooks/exhaustive-deps + [ + job, + analyzersLoading, + connectorsLoading, + visualizersLoading, + pivotsLoading, + ], + ); + + // state + const navigate = useNavigate(); + const location = useLocation(); + const [UIElements, setUIElements] = useState([]); + console.debug( + `location pathname: ${ + location.pathname + }, state - userChanged: ${JSON.stringify( + location?.state?.userChanged, + )}, state - jobReport: #${location?.state?.jobReport.id}`, + ); + + useEffect(() => { + console.debug("JobOverview - check to set default visualizer"); + let visualizerSections = []; + if (Object.values(JobFinalStatuses).includes(job.status)) { + const pageList = job.visualizer_reports.map((report) => report.name); + if (pageList.length > 0) { + visualizerSections = pageList; + } else { + visualizerSections = [NO_VISUALIZER_UI_ELEMENT_CODE]; + } + } else { + visualizerSections = [LOADING_VISUALIZER_UI_ELEMENT_CODE]; + } + console.debug(`visualizerSections: ${JSON.stringify(visualizerSections)}`); + + // check visualizers have been loaded and user didn't changed page + if (visualizerSections !== 0 && !location.state?.userChanged) { + console.debug("updated visualizers"); + if (!subSection) { + console.debug( + `[AUTO REDIRECT] navigate to visualizer: ${ + visualizerSections[0] + }, encoded: ${encodeURIComponent(visualizerSections[0])}`, + ); + // in case no section is selected (ex: from start scan) redirect to a visualizer + navigate( + `/jobs/${job.id}/${JobResultSections.VISUALIZER}/${encodeURIComponent( + visualizerSections[0], + )}`, + { replace: true }, + ); + } else if ( + subSection === LOADING_VISUALIZER_UI_ELEMENT_CODE && + visualizerSections[0] !== LOADING_VISUALIZER_UI_ELEMENT_CODE + ) { + console.debug( + `[AUTO REDIRECT] navigate to visualizer: ${ + visualizerSections[0].id + }, encoded: ${encodeURIComponent(visualizerSections[0])}`, + ); + // in case we are in the loading page and we update the visualizer change page (if they are different from loading) + navigate( + `/jobs/${job.id}/${JobResultSections.VISUALIZER}/${encodeURIComponent( + visualizerSections[0], + )}`, + { replace: true }, + ); + } else if (subSection === NO_VISUALIZER_UI_ELEMENT_CODE) { + console.debug("[AUTO REDIRECT] navigate to raw data - analyzer"); + // in case there is no visualizer redirect to raw data + navigate( + `/jobs/${job.id}/${JobResultSections.RAW}/${rawElements[0].name}`, + { replace: true }, + ); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [job]); + + useEffect(() => { + // this store the ui elements when the frontend download them + console.debug("JobOverview - create/update visualizer components"); + console.debug(job); + let newUIElements = []; + + // 1) generate UI elements in case all visualizers are completed + if ( + Object.values(JobFinalStatuses).includes(job.status) && + job.visualizers_to_execute.length > 0 + ) { + newUIElements = job.visualizer_reports.map((visualizerReport) => ({ + name: visualizerReport.name, + nav: ( +
+ {visualizerReport.name} + {visualizerReport.status !== PluginStatuses.SUCCESS && ( + + )} +
+ ), + report: , + })); + } + + // 2) in case visualizers are running put a loader + if ( + !Object.values(JobFinalStatuses).includes(job.status) && + job.visualizers_to_execute.length > 0 + ) { + newUIElements.push({ + name: LOADING_VISUALIZER_UI_ELEMENT_CODE, + nav: null, + report: ( +
+ +
+ ), + }); + } + + // 3) in case there are no visualizers add a "no data" visualizer + if (job.visualizers_to_execute.length === 0) { + newUIElements.push({ + name: NO_VISUALIZER_UI_ELEMENT_CODE, + nav: null, + report: ( +

+ No visualizers available. You can consult the results in the raw + format.{" "} +

+ ), + }); + } + + setUIElements(newUIElements); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [job]); + + const elementsToShow = isSelectedUI ? UIElements : rawElements; + + return ( + ( + + {/* bar with job id and utilities buttons */} + + +

+ Job #{job.id} + +

+ + + + +
+ {/* job metadata card */} + + + + + + {isRunningJob && ( + + + + )} + +
+ {/* UI/raw switch */} + + + + +
+ +
+
+ {/* reports section */} + + {elementsToShow.sort().map((componentsObject) => ( + + {componentsObject.report} + + ))} + +
+
+ )} + /> + ); +} + +JobOverview.propTypes = { + isRunningJob: PropTypes.bool.isRequired, + job: PropTypes.object.isRequired, + refetch: PropTypes.func.isRequired, + section: PropTypes.string.isRequired, + subSection: PropTypes.string.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/JobResult.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobResult.jsx new file mode 100644 index 0000000..c57bf4f --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/JobResult.jsx @@ -0,0 +1,151 @@ +import React, { useEffect } from "react"; +import useTitle from "react-use/lib/useTitle"; +import { useParams, useLocation } from "react-router-dom"; + +import { Loader } from "@certego/certego-ui"; +import useAxios from "axios-hooks"; +import { WEBSOCKET_JOBS_URI, JOB_BASE_URI } from "../../../constants/apiURLs"; +import { JobOverview } from "./JobOverview"; + +import { + generateJobNotification, + setNotificationFavicon, +} from "../notifications"; + +import { JobFinalStatuses } from "../../../constants/jobConst"; + +export default function JobResult() { + console.debug("JobResult rendered!"); + + // state + const location = useLocation(); + const [initialLoading, setInitialLoading] = React.useState(true); + const [job, setJob] = React.useState(location.state?.jobReport || undefined); + // this state var is used to check if we notified the user, in this way we avoid to notify more than once + const [notified, setNotified] = React.useState(false); + // this state var is used to check if the user changed page, in case he waited the result on the page we avoid the notification + const [toNotify, setToNotify] = React.useState(false); + + // from props + const params = useParams(); + const jobId = params.id; + const { section } = params; + const { subSection } = params; + + const jobWebsocket = React.useRef(); + + const jobIsRunning = + job === undefined || + [ + "pending", + "running", + "analyzers_running", + "connectors_running", + "pivots_running", + "visualizers_running", + "analyzers_completed", + "connectors_completed", + "pivots_completed", + "visualizers_completed", + ].includes(job.status); + + console.debug( + `JobResult - initialLoading: ${initialLoading}, jobIsRunning: ${jobIsRunning}, ` + + `notified: ${notified}, toNotify: ${toNotify}`, + ); + + // useAxios caches the request by default + const [{ data: respData, loading, error }, refetchJob] = useAxios({ + url: `${JOB_BASE_URI}/${jobId}`, + }); + + useEffect(() => { + /* INITIAL SETUP: + - add a focus listener: + * when gain focus set it has been notified and reset the favicon + * when lost focus (blur) we set we can notify the user + - first request with HTTP(S): we avoid to create a ws if not need (ex: old completed jobs) + */ + window.addEventListener("focus", () => { + setNotificationFavicon(false); + setToNotify(false); + }); + window.addEventListener("blur", () => setToNotify(true)); + if (!job && respData && !loading && error == null) setJob(respData); + if (!loading) setInitialLoading(false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [loading]); + + // page title + useTitle( + `IntelOwl | Job (#${jobId}, ${ + // eslint-disable-next-line no-nested-ternary + job ? (job.is_sample ? job.file_name : job.observable_name) : "" + })`, + { restoreOnUnmount: true }, + ); + + /* SETUP WS: + only in case the first request didn't get the job in a final status. + use ws with useRef to avoid to create a ws each render AND create the ws. + only in the last page (section and subSection) or we will create 3 ws, one for each redirect: + jobs/1 -> jobs/1/visualizer -> jobs/1/visualizer/loading + */ + if (job && jobIsRunning && section && subSection && !jobWebsocket.current) { + const websocketUrl = `${ + window.location.protocol === "https:" ? "wss" : "ws" + }://${window.location.hostname}/${WEBSOCKET_JOBS_URI}/${jobId}`; + console.debug(`connect to websocket API: ${websocketUrl}`); + jobWebsocket.current = new WebSocket(websocketUrl); + jobWebsocket.current.onopen = (data) => { + console.debug("ws opened:"); + console.debug(data); + }; + jobWebsocket.current.onclose = (data) => { + console.debug("ws closed:"); + console.debug(data); + }; + jobWebsocket.current.onmessage = (data) => { + console.debug("ws received:"); + console.debug(data); + const jobData = JSON.parse(data.data); + if (Object.values(JobFinalStatuses).includes(jobData.status)) { + jobWebsocket.current.close(1000); + } + setJob(jobData); + }; + jobWebsocket.current.onerror = (data) => { + console.debug("ws error:"); + console.debug(data); + }; + } + + // In case the job terminated and it's not to notify, it means the user waited the result, notification is not needed. + React.useEffect(() => { + if (!jobIsRunning && !toNotify) { + setNotified(true); + } + }, [jobIsRunning, toNotify]); + + // notify the user when the job ends, he left the web page and we didn't notified the user before. + if (!jobIsRunning && toNotify && !notified) { + generateJobNotification(job.observable_name, job.id); + setNotified(true); + } + + return ( + ( + + )} + /> + ); +} diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/bar/JobActionBar.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/bar/JobActionBar.jsx new file mode 100644 index 0000000..dd3bc3c --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/bar/JobActionBar.jsx @@ -0,0 +1,159 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Button } from "reactstrap"; +import { useNavigate } from "react-router-dom"; +import { FaFileDownload } from "react-icons/fa"; + +import { ContentSection, IconButton, addToast } from "@certego/certego-ui"; + +import { SaveAsPlaybookButton } from "./SaveAsPlaybooksForm"; + +import { downloadJobSample, deleteJob } from "../jobApi"; +import { createJob } from "../../../scan/scanApi"; +import { ScanModesNumeric } from "../../../../constants/advancedSettingsConst"; +import { JobResultSections } from "../../../../constants/miscConst"; +import { + DeleteIcon, + CommentIcon, + retryJobIcon, + downloadReportIcon, +} from "../../../common/icon/icons"; + +export function JobActionsBar({ job }) { + // routers + const navigate = useNavigate(); + + // callbacks + const onDeleteBtnClick = async () => { + const success = await deleteJob(job.id); + if (!success) return; + addToast("Redirecting...", null, "secondary"); + setTimeout(() => navigate(-1), 250); + }; + + const fileDownload = (blob, filename) => { + // create URL blob and a hidden tag to serve file for download + const fileLink = document.createElement("a"); + fileLink.href = window.URL.createObjectURL(blob); + fileLink.rel = "noopener,noreferrer"; + fileLink.download = `${filename}`; + // triggers the click event + fileLink.click(); + }; + + const onDownloadSampleBtnClick = async () => { + const blob = await downloadJobSample(job.id); + if (!blob) return; + let filename = "file"; + if (job?.file_name) { + // it forces the name of the downloaded file + filename = `${job.file_name}`; + } + fileDownload(blob, filename); + }; + + const handleRetry = async () => { + if (job.is_sample) { + addToast( + "Rescan File!", + "It's not possible to repeat a sample analysis", + "warning", + false, + 2000, + ); + } else { + addToast("Retrying the same job...", null, "spinner", false, 2000); + const response = await createJob( + [job.observable_name], + job.observable_classification, + job.playbook_requested, + job.analyzers_requested, + job.connectors_requested, + job.runtime_configuration, + job.tags.map((optTag) => optTag.label), + job.tlp, + ScanModesNumeric.FORCE_NEW_ANALYSIS, + 0, + ); + setTimeout( + () => + navigate( + `/jobs/${response.jobIds[0]}/${JobResultSections.VISUALIZER}/`, + ), + 1000, + ); + } + }; + + const onDownloadReport = () => { + if (job) { + const blob = new Blob([JSON.stringify(job)], { type: "text/json" }); + if (!blob) return; + fileDownload(blob, `job#${job.id}_report.json`); + } + }; + + const commentIcon = () => ; + return ( + + navigate(`/jobs/${job.id}/comments`)} + title="Comments" + titlePlacement="top" + /> + {job.permissions?.delete && ( + + )} + + + {job?.is_sample && ( + + )} + + + ); +} + +JobActionsBar.propTypes = { + job: PropTypes.object.isRequired, +}; diff --git a/Submodules/IntelOwl/frontend/src/components/jobs/result/bar/SaveAsPlaybooksForm.jsx b/Submodules/IntelOwl/frontend/src/components/jobs/result/bar/SaveAsPlaybooksForm.jsx new file mode 100644 index 0000000..c52a0d1 --- /dev/null +++ b/Submodules/IntelOwl/frontend/src/components/jobs/result/bar/SaveAsPlaybooksForm.jsx @@ -0,0 +1,159 @@ +import React from "react"; +import { Col, FormGroup, Label, Button, Spinner, Input } from "reactstrap"; +import { Form, Formik } from "formik"; +import { IoMdSave } from "react-icons/io"; +import PropTypes from "prop-types"; + +import { addToast, PopupFormButton } from "@certego/certego-ui"; + +import { saveJobAsPlaybook } from "./jobBarApi"; + +// constants +const initialValues = { + name: "", + description: "", + analyzers: [], + connectors: [], + pivots: [], + runtimeConfiguration: {}, +}; + +// methods +const onValidate = (values) => { + const minLength = 3; + const errors = {}; + if (!values.name) { + errors.name = "This field is required."; + } else if (values.name.length < minLength) { + errors.name = `This field must be at least ${minLength} characters long`; + } + if (!values.description) { + errors.description = "This field is required."; + } + return errors; +}; + +// Invitation Form +export function SaveAsPlaybookForm({ onFormSubmit }) { + console.debug("InvitationForm rendered!"); + + const onSubmit = React.useCallback( + async (values, formik) => { + try { + await saveJobAsPlaybook(values); + onFormSubmit(); + } catch (error) { + addToast(Error!, error.parsedMsg, "warning"); + } finally { + formik.setSubmitting(false); + } + }, + [onFormSubmit], + ); + + return ( + + {(formik) => ( +
+ + +
+ + + {formik.touched.name && ( + {formik.errors.name} + )} +
+ +
+ + \n\n return (\n
\n
\n handleSetIsExpanded()}\n style={{ cursor: \"pointer\" }}\n >Snippets\n handleSetIsExpanded()}\n style={{ border: \"none\", background: \"none\" }}\n title={isExpanded ? \"Collapse operation\" : \"Expand operation\"}\n >\n \n \n \n \n
\n {\n isExpanded &&
\n
\n {\n snippetGenerators.entrySeq().map(([key, gen]) => {\n return (
handleGenChange(key)}>\n

{gen.get(\"title\")}

\n
)\n })\n }\n
\n
\n \n
\n
\n {SnippetComponent}\n
\n
\n }\n
\n ) \n}\n\nRequestSnippets.propTypes = {\n request: PropTypes.object.isRequired,\n requestSnippetsSelectors: PropTypes.object.isRequired,\n getConfigs: PropTypes.object.isRequired,\n requestSnippetsActions: PropTypes.object,\n}\n\nexport default RequestSnippets\n","import { createSelector } from \"reselect\"\nimport { Map } from \"immutable\"\n\nconst state = state => state || Map()\n\nexport const getGenerators = createSelector(\n state,\n state => {\n const languageKeys = state\n .get(\"languages\")\n const generators = state\n .get(\"generators\", Map())\n if(!languageKeys || languageKeys.isEmpty()) {\n return generators\n }\n return generators\n .filter((v, key) => languageKeys.includes(key))\n }\n)\n\nexport const getSnippetGenerators = (state) => ({ fn }) => {\n const getGenFn = (key) => fn[`requestSnippetGenerator_${key}`]\n return getGenerators(state)\n .map((gen, key) => {\n const genFn = getGenFn(key)\n if(typeof genFn !== \"function\") {\n return null\n }\n\n return gen.set(\"fn\", genFn)\n })\n .filter(v => v)\n}\n\nexport const getActiveLanguage = createSelector(\n state,\n state => state\n .get(\"activeLanguage\")\n)\n\nexport const getDefaultExpanded = createSelector(\n state,\n state => state\n .get(\"defaultExpanded\")\n)\n","import PropTypes from \"prop-types\"\nimport React, { Component } from \"react\"\n\nimport { componentDidCatch } from \"../fn\"\nimport Fallback from \"./fallback\"\n\nexport class ErrorBoundary extends Component {\n static getDerivedStateFromError(error) {\n return { hasError: true, error }\n }\n\n constructor(...args) {\n super(...args)\n this.state = { hasError: false, error: null }\n }\n\n componentDidCatch(error, errorInfo) {\n this.props.fn.componentDidCatch(error, errorInfo)\n }\n\n render() {\n const { getComponent, targetName, children } = this.props\n\n if (this.state.hasError) {\n const FallbackComponent = getComponent(\"Fallback\")\n return \n }\n\n return children\n }\n}\nErrorBoundary.propTypes = {\n targetName: PropTypes.string,\n getComponent: PropTypes.func,\n fn: PropTypes.object,\n children: PropTypes.oneOfType([\n PropTypes.arrayOf(PropTypes.node),\n PropTypes.node,\n ])\n}\nErrorBoundary.defaultProps = {\n targetName: \"this component\",\n getComponent: () => Fallback,\n fn: {\n componentDidCatch,\n },\n children: null,\n}\n\nexport default ErrorBoundary\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nconst Fallback = ({ name }) => (\n
\n 😱 Could not render { name === \"t\" ? \"this component\" : name }, see the console.\n
\n)\nFallback.propTypes = {\n name: PropTypes.string.isRequired,\n}\n\nexport default Fallback\n","import React, { Component } from \"react\"\n\nexport const componentDidCatch = console.error\n\nconst isClassComponent = component => component.prototype && component.prototype.isReactComponent\n\nexport const withErrorBoundary = (getSystem) => (WrappedComponent) => {\n const { getComponent, fn } = getSystem()\n const ErrorBoundary = getComponent(\"ErrorBoundary\")\n const targetName = fn.getDisplayName(WrappedComponent)\n\n class WithErrorBoundary extends Component {\n render() {\n return (\n \n \n \n )\n }\n }\n WithErrorBoundary.displayName = `WithErrorBoundary(${targetName})`\n if (isClassComponent(WrappedComponent)) {\n /**\n * We need to handle case of class components defining a `mapStateToProps` public method.\n * Components with `mapStateToProps` public method cannot be wrapped.\n */\n WithErrorBoundary.prototype.mapStateToProps = WrappedComponent.prototype.mapStateToProps\n }\n\n return WithErrorBoundary\n}\n\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_fill_07ef3114__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_zipObject_c74f1c14__[\"default\"] });","import zipObject from \"lodash/zipObject\"\n\nimport ErrorBoundary from \"./components/error-boundary\"\nimport Fallback from \"./components/fallback\"\nimport { componentDidCatch, withErrorBoundary } from \"./fn\"\n\nconst safeRenderPlugin = ({componentList = [], fullOverride = false} = {}) => ({ getSystem }) => {\n const defaultComponentList = [\n \"App\",\n \"BaseLayout\",\n \"VersionPragmaFilter\",\n \"InfoContainer\",\n \"ServersContainer\",\n \"SchemesContainer\",\n \"AuthorizeBtnContainer\",\n \"FilterContainer\",\n \"Operations\",\n \"OperationContainer\",\n \"parameters\",\n \"responses\",\n \"OperationServers\",\n \"Models\",\n \"ModelWrapper\",\n ]\n const mergedComponentList = fullOverride ? componentList : [...defaultComponentList, ...componentList]\n const wrapFactory = (Original, { fn }) => fn.withErrorBoundary(Original)\n const wrapComponents = zipObject(mergedComponentList, Array(mergedComponentList.length).fill(wrapFactory))\n\n return {\n fn: {\n componentDidCatch,\n withErrorBoundary: withErrorBoundary(getSystem),\n },\n components: {\n ErrorBoundary,\n Fallback,\n },\n wrapComponents,\n }\n}\n\nexport default safeRenderPlugin\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_randexp__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_isEmpty_e109fd6b__[\"default\"] });","import XML from \"xml\"\nimport RandExp from \"randexp\"\nimport isEmpty from \"lodash/isEmpty\"\nimport { objectify, isFunc, normalizeArray, deeplyStripKey } from \"core/utils\"\n\nimport memoizeN from \"../../../helpers/memoizeN\"\n\nconst generateStringFromRegex = (pattern) => {\n try {\n const randexp = new RandExp(pattern)\n return randexp.gen()\n } catch (e) {\n // Invalid regex should not cause a crash (regex syntax varies across languages)\n return \"string\"\n }\n}\n\nconst primitives = {\n \"string\": (schema) => schema.pattern ? generateStringFromRegex(schema.pattern) : \"string\",\n \"string_email\": () => \"user@example.com\",\n \"string_date-time\": () => new Date().toISOString(),\n \"string_date\": () => new Date().toISOString().substring(0, 10),\n \"string_uuid\": () => \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n \"string_hostname\": () => \"example.com\",\n \"string_ipv4\": () => \"198.51.100.42\",\n \"string_ipv6\": () => \"2001:0db8:5b96:0000:0000:426f:8e17:642a\",\n \"number\": () => 0,\n \"number_float\": () => 0.0,\n \"integer\": () => 0,\n \"boolean\": (schema) => typeof schema.default === \"boolean\" ? schema.default : true\n}\n\nconst primitive = (schema) => {\n schema = objectify(schema)\n let { type, format } = schema\n\n let fn = primitives[`${type}_${format}`] || primitives[type]\n\n if(isFunc(fn))\n return fn(schema)\n\n return \"Unknown Type: \" + schema.type\n}\n\n// do a couple of quick sanity tests to ensure the value\n// looks like a $$ref that swagger-client generates.\nconst sanitizeRef = (value) => deeplyStripKey(value, \"$$ref\", (val) =>\n typeof val === \"string\" && val.indexOf(\"#\") > -1)\n\nconst objectContracts = [\"maxProperties\", \"minProperties\"]\nconst arrayContracts = [\"minItems\", \"maxItems\"]\nconst numberContracts = [\n \"minimum\",\n \"maximum\",\n \"exclusiveMinimum\",\n \"exclusiveMaximum\"\n]\nconst stringContracts = [\"minLength\", \"maxLength\"]\n\nconst liftSampleHelper = (oldSchema, target, config = {}) => {\n const setIfNotDefinedInTarget = (key) => {\n if(target[key] === undefined && oldSchema[key] !== undefined) {\n target[key] = oldSchema[key]\n }\n }\n\n [\n \"example\",\n \"default\",\n \"enum\",\n \"xml\",\n \"type\",\n ...objectContracts,\n ...arrayContracts,\n ...numberContracts,\n ...stringContracts,\n ].forEach(key => setIfNotDefinedInTarget(key))\n\n if(oldSchema.required !== undefined && Array.isArray(oldSchema.required)) {\n if(target.required === undefined || !target.required.length) {\n target.required = []\n }\n oldSchema.required.forEach(key => {\n if(target.required.includes(key)) {\n return\n }\n target.required.push(key)\n })\n }\n if(oldSchema.properties) {\n if(!target.properties) {\n target.properties = {}\n }\n let props = objectify(oldSchema.properties)\n for (let propName in props) {\n if (!Object.prototype.hasOwnProperty.call(props, propName)) {\n continue\n }\n if ( props[propName] && props[propName].deprecated ) {\n continue\n }\n if ( props[propName] && props[propName].readOnly && !config.includeReadOnly ) {\n continue\n }\n if ( props[propName] && props[propName].writeOnly && !config.includeWriteOnly ) {\n continue\n }\n if(!target.properties[propName]) {\n target.properties[propName] = props[propName]\n if(!oldSchema.required && Array.isArray(oldSchema.required) && oldSchema.required.indexOf(propName) !== -1) {\n if(!target.required) {\n target.required = [propName]\n } else {\n target.required.push(propName)\n }\n }\n }\n }\n }\n if(oldSchema.items) {\n if(!target.items) {\n target.items = {}\n }\n target.items = liftSampleHelper(oldSchema.items, target.items, config)\n }\n\n return target\n}\n\nexport const sampleFromSchemaGeneric = (schema, config={}, exampleOverride = undefined, respectXML = false) => {\n if(schema && isFunc(schema.toJS))\n schema = schema.toJS()\n let usePlainValue = exampleOverride !== undefined || schema && schema.example !== undefined || schema && schema.default !== undefined\n // first check if there is the need of combining this schema with others required by allOf\n const hasOneOf = !usePlainValue && schema && schema.oneOf && schema.oneOf.length > 0\n const hasAnyOf = !usePlainValue && schema && schema.anyOf && schema.anyOf.length > 0\n if(!usePlainValue && (hasOneOf || hasAnyOf)) {\n const schemaToAdd = objectify(hasOneOf\n ? schema.oneOf[0]\n : schema.anyOf[0]\n )\n liftSampleHelper(schemaToAdd, schema, config)\n if(!schema.xml && schemaToAdd.xml) {\n schema.xml = schemaToAdd.xml\n }\n if(schema.example !== undefined && schemaToAdd.example !== undefined) {\n usePlainValue = true\n } else if(schemaToAdd.properties) {\n if(!schema.properties) {\n schema.properties = {}\n }\n let props = objectify(schemaToAdd.properties)\n for (let propName in props) {\n if (!Object.prototype.hasOwnProperty.call(props, propName)) {\n continue\n }\n if ( props[propName] && props[propName].deprecated ) {\n continue\n }\n if ( props[propName] && props[propName].readOnly && !config.includeReadOnly ) {\n continue\n }\n if ( props[propName] && props[propName].writeOnly && !config.includeWriteOnly ) {\n continue\n }\n if(!schema.properties[propName]) {\n schema.properties[propName] = props[propName]\n if(!schemaToAdd.required && Array.isArray(schemaToAdd.required) && schemaToAdd.required.indexOf(propName) !== -1) {\n if(!schema.required) {\n schema.required = [propName]\n } else {\n schema.required.push(propName)\n }\n }\n }\n }\n }\n }\n const _attr = {}\n let { xml, type, example, properties, additionalProperties, items } = schema || {}\n let { includeReadOnly, includeWriteOnly } = config\n xml = xml || {}\n let { name, prefix, namespace } = xml\n let displayName\n let res = {}\n\n // set xml naming and attributes\n if(respectXML) {\n name = name || \"notagname\"\n // add prefix to name if exists\n displayName = (prefix ? prefix + \":\" : \"\") + name\n if ( namespace ) {\n //add prefix to namespace if exists\n let namespacePrefix = prefix ? ( \"xmlns:\" + prefix ) : \"xmlns\"\n _attr[namespacePrefix] = namespace\n }\n }\n\n // init xml default response sample obj\n if(respectXML) {\n res[displayName] = []\n }\n\n const schemaHasAny = (keys) => keys.some(key => Object.prototype.hasOwnProperty.call(schema, key))\n // try recover missing type\n if(schema && !type) {\n if(properties || additionalProperties || schemaHasAny(objectContracts)) {\n type = \"object\"\n } else if(items || schemaHasAny(arrayContracts)) {\n type = \"array\"\n } else if(schemaHasAny(numberContracts)) {\n type = \"number\"\n schema.type = \"number\"\n } else if(!usePlainValue && !schema.enum){\n // implicit cover schemaHasAny(stringContracts) or A schema without a type matches any data type is:\n // components:\n // schemas:\n // AnyValue:\n // anyOf:\n // - type: string\n // - type: number\n // - type: integer\n // - type: boolean\n // - type: array\n // items: {}\n // - type: object\n //\n // which would resolve to type: string\n type = \"string\"\n schema.type = \"string\"\n }\n }\n\n const handleMinMaxItems = (sampleArray) => {\n if (schema?.maxItems !== null && schema?.maxItems !== undefined) {\n sampleArray = sampleArray.slice(0, schema?.maxItems)\n }\n if (schema?.minItems !== null && schema?.minItems !== undefined) {\n let i = 0\n while (sampleArray.length < schema?.minItems) {\n sampleArray.push(sampleArray[i++ % sampleArray.length])\n }\n }\n return sampleArray\n }\n\n // add to result helper init for xml or json\n const props = objectify(properties)\n let addPropertyToResult\n let propertyAddedCounter = 0\n\n const hasExceededMaxProperties = () => schema\n && schema.maxProperties !== null && schema.maxProperties !== undefined\n && propertyAddedCounter >= schema.maxProperties\n\n const requiredPropertiesToAdd = () => {\n if(!schema || !schema.required) {\n return 0\n }\n let addedCount = 0\n if(respectXML) {\n schema.required.forEach(key => addedCount +=\n res[key] === undefined\n ? 0\n : 1\n )\n } else {\n schema.required.forEach(key => addedCount +=\n res[displayName]?.find(x => x[key] !== undefined) === undefined\n ? 0\n : 1\n )\n }\n return schema.required.length - addedCount\n }\n\n const isOptionalProperty = (propName) => {\n if(!schema || !schema.required || !schema.required.length) {\n return true\n }\n return !schema.required.includes(propName)\n }\n\n const canAddProperty = (propName) => {\n if(!schema || schema.maxProperties === null || schema.maxProperties === undefined) {\n return true\n }\n if(hasExceededMaxProperties()) {\n return false\n }\n if(!isOptionalProperty(propName)) {\n return true\n }\n return (schema.maxProperties - propertyAddedCounter - requiredPropertiesToAdd()) > 0\n }\n\n if(respectXML) {\n addPropertyToResult = (propName, overrideE = undefined) => {\n if(schema && props[propName]) {\n // case it is an xml attribute\n props[propName].xml = props[propName].xml || {}\n\n if (props[propName].xml.attribute) {\n const enumAttrVal = Array.isArray(props[propName].enum)\n ? props[propName].enum[0]\n : undefined\n const attrExample = props[propName].example\n const attrDefault = props[propName].default\n\n if(attrExample !== undefined) {\n _attr[props[propName].xml.name || propName] = attrExample\n } else if(attrDefault !== undefined) {\n _attr[props[propName].xml.name || propName] = attrDefault\n } else if(enumAttrVal !== undefined) {\n _attr[props[propName].xml.name || propName] = enumAttrVal\n } else {\n _attr[props[propName].xml.name || propName] = primitive(props[propName])\n }\n\n return\n }\n props[propName].xml.name = props[propName].xml.name || propName\n } else if(!props[propName] && additionalProperties !== false) {\n // case only additionalProperty that is not defined in schema\n props[propName] = {\n xml: {\n name: propName\n }\n }\n }\n\n let t = sampleFromSchemaGeneric(schema && props[propName] || undefined, config, overrideE, respectXML)\n if(!canAddProperty(propName)) {\n return\n }\n\n propertyAddedCounter++\n if (Array.isArray(t)) {\n res[displayName] = res[displayName].concat(t)\n } else {\n res[displayName].push(t)\n }\n }\n } else {\n addPropertyToResult = (propName, overrideE) => {\n if(!canAddProperty(propName)) {\n return\n }\n res[propName] = sampleFromSchemaGeneric(props[propName], config, overrideE, respectXML)\n propertyAddedCounter++\n }\n }\n\n // check for plain value and if found use it to generate sample from it\n if(usePlainValue) {\n let sample\n if(exampleOverride !== undefined) {\n sample = sanitizeRef(exampleOverride)\n } else if(example !== undefined) {\n sample = sanitizeRef(example)\n } else {\n sample = sanitizeRef(schema.default)\n }\n\n // if json just return\n if(!respectXML) {\n // spacial case yaml parser can not know about\n if(typeof sample === \"number\" && type === \"string\") {\n return `${sample}`\n }\n // return if sample does not need any parsing\n if(typeof sample !== \"string\" || type === \"string\") {\n return sample\n }\n // check if sample is parsable or just a plain string\n try {\n return JSON.parse(sample)\n } catch(e) {\n // sample is just plain string return it\n return sample\n }\n }\n\n // recover missing type\n if(!schema) {\n type = Array.isArray(sample) ? \"array\" : typeof sample\n }\n\n // generate xml sample recursively for array case\n if(type === \"array\") {\n if (!Array.isArray(sample)) {\n if(typeof sample === \"string\") {\n return sample\n }\n sample = [sample]\n }\n const itemSchema = schema\n ? schema.items\n : undefined\n if(itemSchema) {\n itemSchema.xml = itemSchema.xml || xml || {}\n itemSchema.xml.name = itemSchema.xml.name || xml.name\n }\n let itemSamples = sample\n .map(s => sampleFromSchemaGeneric(itemSchema, config, s, respectXML))\n itemSamples = handleMinMaxItems(itemSamples)\n if(xml.wrapped) {\n res[displayName] = itemSamples\n if (!isEmpty(_attr)) {\n res[displayName].push({_attr: _attr})\n }\n }\n else {\n res = itemSamples\n }\n return res\n }\n\n // generate xml sample recursively for object case\n if(type === \"object\") {\n // case literal example\n if(typeof sample === \"string\") {\n return sample\n }\n for (let propName in sample) {\n if (!Object.prototype.hasOwnProperty.call(sample, propName)) {\n continue\n }\n if (schema && props[propName] && props[propName].readOnly && !includeReadOnly) {\n continue\n }\n if (schema && props[propName] && props[propName].writeOnly && !includeWriteOnly) {\n continue\n }\n if (schema && props[propName] && props[propName].xml && props[propName].xml.attribute) {\n _attr[props[propName].xml.name || propName] = sample[propName]\n continue\n }\n addPropertyToResult(propName, sample[propName])\n }\n if (!isEmpty(_attr)) {\n res[displayName].push({_attr: _attr})\n }\n\n return res\n }\n\n res[displayName] = !isEmpty(_attr) ? [{_attr: _attr}, sample] : sample\n return res\n }\n\n // use schema to generate sample\n\n if(type === \"object\") {\n for (let propName in props) {\n if (!Object.prototype.hasOwnProperty.call(props, propName)) {\n continue\n }\n if ( props[propName] && props[propName].deprecated ) {\n continue\n }\n if ( props[propName] && props[propName].readOnly && !includeReadOnly ) {\n continue\n }\n if ( props[propName] && props[propName].writeOnly && !includeWriteOnly ) {\n continue\n }\n addPropertyToResult(propName)\n }\n if (respectXML && _attr) {\n res[displayName].push({_attr: _attr})\n }\n\n if(hasExceededMaxProperties()) {\n return res\n }\n\n if ( additionalProperties === true ) {\n if(respectXML) {\n res[displayName].push({additionalProp: \"Anything can be here\"})\n } else {\n res.additionalProp1 = {}\n }\n propertyAddedCounter++\n } else if ( additionalProperties ) {\n const additionalProps = objectify(additionalProperties)\n const additionalPropSample = sampleFromSchemaGeneric(additionalProps, config, undefined, respectXML)\n\n if(respectXML && additionalProps.xml && additionalProps.xml.name && additionalProps.xml.name !== \"notagname\")\n {\n res[displayName].push(additionalPropSample)\n } else {\n const toGenerateCount = schema.minProperties !== null && schema.minProperties !== undefined && propertyAddedCounter < schema.minProperties\n ? schema.minProperties - propertyAddedCounter\n : 3\n for (let i = 1; i <= toGenerateCount; i++) {\n if(hasExceededMaxProperties()) {\n return res\n }\n if(respectXML) {\n const temp = {}\n temp[\"additionalProp\" + i] = additionalPropSample[\"notagname\"]\n res[displayName].push(temp)\n } else {\n res[\"additionalProp\" + i] = additionalPropSample\n }\n propertyAddedCounter++\n }\n }\n }\n return res\n }\n\n if(type === \"array\") {\n if (!items) {\n return\n }\n\n let sampleArray\n if(respectXML) {\n items.xml = items.xml || schema?.xml || {}\n items.xml.name = items.xml.name || xml.name\n }\n\n if(Array.isArray(items.anyOf)) {\n sampleArray = items.anyOf.map(i => sampleFromSchemaGeneric(liftSampleHelper(items, i, config), config, undefined, respectXML))\n } else if(Array.isArray(items.oneOf)) {\n sampleArray = items.oneOf.map(i => sampleFromSchemaGeneric(liftSampleHelper(items, i, config), config, undefined, respectXML))\n } else if(!respectXML || respectXML && xml.wrapped) {\n sampleArray = [sampleFromSchemaGeneric(items, config, undefined, respectXML)]\n } else {\n return sampleFromSchemaGeneric(items, config, undefined, respectXML)\n }\n sampleArray = handleMinMaxItems(sampleArray)\n if(respectXML && xml.wrapped) {\n res[displayName] = sampleArray\n if (!isEmpty(_attr)) {\n res[displayName].push({_attr: _attr})\n }\n return res\n }\n return sampleArray\n }\n\n let value\n if (schema && Array.isArray(schema.enum)) {\n //display enum first value\n value = normalizeArray(schema.enum)[0]\n } else if(schema) {\n // display schema default\n value = primitive(schema)\n if(typeof value === \"number\") {\n let min = schema.minimum\n if(min !== undefined && min !== null) {\n if(schema.exclusiveMinimum) {\n min++\n }\n value = min\n }\n let max = schema.maximum\n if(max !== undefined && max !== null) {\n if(schema.exclusiveMaximum) {\n max--\n }\n value = max\n }\n }\n if(typeof value === \"string\") {\n if (schema.maxLength !== null && schema.maxLength !== undefined) {\n value = value.slice(0, schema.maxLength)\n }\n if (schema.minLength !== null && schema.minLength !== undefined) {\n let i = 0\n while (value.length < schema.minLength) {\n value += value[i++ % value.length]\n }\n }\n }\n } else {\n return\n }\n if (type === \"file\") {\n return\n }\n\n if(respectXML) {\n res[displayName] = !isEmpty(_attr) ? [{_attr: _attr}, value] : value\n return res\n }\n\n return value\n}\n\nexport const inferSchema = (thing) => {\n if(thing.schema)\n thing = thing.schema\n\n if(thing.properties) {\n thing.type = \"object\"\n }\n\n return thing // Hopefully this will have something schema like in it... `type` for example\n}\n\nexport const createXMLExample = (schema, config, o) => {\n const json = sampleFromSchemaGeneric(schema, config, o, true)\n if (!json) { return }\n if(typeof json === \"string\") {\n return json\n }\n return XML(json, { declaration: true, indent: \"\\t\" })\n}\n\nexport const sampleFromSchema = (schema, config, o) =>\n sampleFromSchemaGeneric(schema, config, o, false)\n\nconst resolver = (arg1, arg2, arg3) => [arg1, JSON.stringify(arg2), JSON.stringify(arg3)]\n\nexport const memoizedCreateXMLExample = memoizeN(createXMLExample, resolver)\n\nexport const memoizedSampleFromSchema = memoizeN(sampleFromSchema, resolver)\n","import * as fn from \"./fn\"\n\nexport default function () {\n return { fn }\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_asyncToGenerator_db854086__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_regenerator_4d88cac3__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_promise_047dc8e8__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_date_now_1bf78713__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_isString_e6fa8a5b__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_debounce_3540babe__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_set_b4b15ee5__[\"default\"] });","import YAML, { JSON_SCHEMA } from \"js-yaml\"\nimport { Map } from \"immutable\"\nimport parseUrl from \"url-parse\"\nimport { serializeError } from \"serialize-error\"\nimport isString from \"lodash/isString\"\nimport debounce from \"lodash/debounce\"\nimport set from \"lodash/set\"\nimport { paramToValue, isEmptyValue } from \"core/utils\"\n\n// Actions conform to FSA (flux-standard-actions)\n// {type: string,payload: Any|Error, meta: obj, error: bool}\n\nexport const UPDATE_SPEC = \"spec_update_spec\"\nexport const UPDATE_URL = \"spec_update_url\"\nexport const UPDATE_JSON = \"spec_update_json\"\nexport const UPDATE_PARAM = \"spec_update_param\"\nexport const UPDATE_EMPTY_PARAM_INCLUSION = \"spec_update_empty_param_inclusion\"\nexport const VALIDATE_PARAMS = \"spec_validate_param\"\nexport const SET_RESPONSE = \"spec_set_response\"\nexport const SET_REQUEST = \"spec_set_request\"\nexport const SET_MUTATED_REQUEST = \"spec_set_mutated_request\"\nexport const LOG_REQUEST = \"spec_log_request\"\nexport const CLEAR_RESPONSE = \"spec_clear_response\"\nexport const CLEAR_REQUEST = \"spec_clear_request\"\nexport const CLEAR_VALIDATE_PARAMS = \"spec_clear_validate_param\"\nexport const UPDATE_OPERATION_META_VALUE = \"spec_update_operation_meta_value\"\nexport const UPDATE_RESOLVED = \"spec_update_resolved\"\nexport const UPDATE_RESOLVED_SUBTREE = \"spec_update_resolved_subtree\"\nexport const SET_SCHEME = \"set_scheme\"\n\nconst toStr = (str) => isString(str) ? str : \"\"\n\nexport function updateSpec(spec) {\n const cleanSpec = (toStr(spec)).replace(/\\t/g, \" \")\n if(typeof spec === \"string\") {\n return {\n type: UPDATE_SPEC,\n payload: cleanSpec\n }\n }\n}\n\nexport function updateResolved(spec) {\n return {\n type: UPDATE_RESOLVED,\n payload: spec\n }\n}\n\nexport function updateUrl(url) {\n return {type: UPDATE_URL, payload: url}\n}\n\nexport function updateJsonSpec(json) {\n return {type: UPDATE_JSON, payload: json}\n}\n\nexport const parseToJson = (str) => ({specActions, specSelectors, errActions}) => {\n let { specStr } = specSelectors\n\n let json = null\n try {\n str = str || specStr()\n errActions.clear({ source: \"parser\" })\n json = YAML.load(str, { schema: JSON_SCHEMA })\n } catch(e) {\n // TODO: push error to state\n console.error(e)\n return errActions.newSpecErr({\n source: \"parser\",\n level: \"error\",\n message: e.reason,\n line: e.mark && e.mark.line ? e.mark.line + 1 : undefined\n })\n }\n if(json && typeof json === \"object\") {\n return specActions.updateJsonSpec(json)\n }\n return {}\n}\n\nlet hasWarnedAboutResolveSpecDeprecation = false\n\nexport const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST = {} }, getConfigs}) => {\n if(!hasWarnedAboutResolveSpecDeprecation) {\n console.warn(`specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!`)\n hasWarnedAboutResolveSpecDeprecation = true\n }\n\n const {\n modelPropertyMacro,\n parameterMacro,\n requestInterceptor,\n responseInterceptor\n } = getConfigs()\n\n if(typeof(json) === \"undefined\") {\n json = specSelectors.specJson()\n }\n if(typeof(url) === \"undefined\") {\n url = specSelectors.url()\n }\n\n let getLineNumberForPath = AST.getLineNumberForPath ? AST.getLineNumberForPath : () => undefined\n\n let specStr = specSelectors.specStr()\n\n return resolve({\n fetch,\n spec: json,\n baseDoc: url,\n modelPropertyMacro,\n parameterMacro,\n requestInterceptor,\n responseInterceptor\n }).then( ({spec, errors}) => {\n errActions.clear({\n type: \"thrown\"\n })\n if(Array.isArray(errors) && errors.length > 0) {\n let preparedErrors = errors\n .map(err => {\n console.error(err)\n err.line = err.fullPath ? getLineNumberForPath(specStr, err.fullPath) : null\n err.path = err.fullPath ? err.fullPath.join(\".\") : null\n err.level = \"error\"\n err.type = \"thrown\"\n err.source = \"resolver\"\n Object.defineProperty(err, \"message\", { enumerable: true, value: err.message })\n return err\n })\n errActions.newThrownErrBatch(preparedErrors)\n }\n\n return specActions.updateResolved(spec)\n })\n}\n\nlet requestBatch = []\n\nconst debResolveSubtrees = debounce(async () => {\n const system = requestBatch.system // Just a reference to the \"latest\" system\n\n if(!system) {\n console.error(\"debResolveSubtrees: don't have a system to operate on, aborting.\")\n return\n }\n const {\n errActions,\n errSelectors,\n fn: {\n resolveSubtree,\n fetch,\n AST = {}\n },\n specSelectors,\n specActions,\n } = system\n\n if(!resolveSubtree) {\n console.error(\"Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.\")\n return\n }\n\n let getLineNumberForPath = AST.getLineNumberForPath ? AST.getLineNumberForPath : () => undefined\n\n const specStr = specSelectors.specStr()\n\n const {\n modelPropertyMacro,\n parameterMacro,\n requestInterceptor,\n responseInterceptor\n } = system.getConfigs()\n\n try {\n var batchResult = await requestBatch.reduce(async (prev, path) => {\n const { resultMap, specWithCurrentSubtrees } = await prev\n const { errors, spec } = await resolveSubtree(specWithCurrentSubtrees, path, {\n baseDoc: specSelectors.url(),\n modelPropertyMacro,\n parameterMacro,\n requestInterceptor,\n responseInterceptor\n })\n\n if(errSelectors.allErrors().size) {\n errActions.clearBy(err => {\n // keep if...\n return err.get(\"type\") !== \"thrown\" // it's not a thrown error\n || err.get(\"source\") !== \"resolver\" // it's not a resolver error\n || !err.get(\"fullPath\").every((key, i) => key === path[i] || path[i] === undefined) // it's not within the path we're resolving\n })\n }\n\n if(Array.isArray(errors) && errors.length > 0) {\n let preparedErrors = errors\n .map(err => {\n err.line = err.fullPath ? getLineNumberForPath(specStr, err.fullPath) : null\n err.path = err.fullPath ? err.fullPath.join(\".\") : null\n err.level = \"error\"\n err.type = \"thrown\"\n err.source = \"resolver\"\n Object.defineProperty(err, \"message\", { enumerable: true, value: err.message })\n return err\n })\n errActions.newThrownErrBatch(preparedErrors)\n }\n\n if (spec && specSelectors.isOAS3() && path[0] === \"components\" && path[1] === \"securitySchemes\") {\n // Resolve OIDC URLs if present\n await Promise.all(Object.values(spec)\n .filter((scheme) => scheme.type === \"openIdConnect\")\n .map(async (oidcScheme) => {\n const req = {\n url: oidcScheme.openIdConnectUrl,\n requestInterceptor: requestInterceptor,\n responseInterceptor: responseInterceptor\n }\n try {\n const res = await fetch(req)\n if (res instanceof Error || res.status >= 400) {\n console.error(res.statusText + \" \" + req.url)\n } else {\n oidcScheme.openIdConnectData = JSON.parse(res.text)\n }\n } catch (e) {\n console.error(e)\n }\n }))\n }\n set(resultMap, path, spec)\n set(specWithCurrentSubtrees, path, spec)\n\n return {\n resultMap,\n specWithCurrentSubtrees\n }\n }, Promise.resolve({\n resultMap: (specSelectors.specResolvedSubtree([]) || Map()).toJS(),\n specWithCurrentSubtrees: specSelectors.specJson().toJS()\n }))\n\n delete requestBatch.system\n requestBatch = [] // Clear stack\n } catch(e) {\n console.error(e)\n }\n\n specActions.updateResolvedSubtree([], batchResult.resultMap)\n}, 35)\n\nexport const requestResolvedSubtree = path => system => {\n // poor-man's array comparison\n // if this ever inadequate, this should be rewritten to use Im.List\n const isPathAlreadyBatched = requestBatch\n .map(arr => arr.join(\"@@\"))\n .indexOf(path.join(\"@@\")) > -1\n\n if(isPathAlreadyBatched) {\n return\n }\n\n requestBatch.push(path)\n requestBatch.system = system\n debResolveSubtrees()\n}\n\nexport function changeParam( path, paramName, paramIn, value, isXml ){\n return {\n type: UPDATE_PARAM,\n payload:{ path, value, paramName, paramIn, isXml }\n }\n}\n\nexport function changeParamByIdentity( pathMethod, param, value, isXml ){\n return {\n type: UPDATE_PARAM,\n payload:{ path: pathMethod, param, value, isXml }\n }\n}\n\nexport const updateResolvedSubtree = (path, value) => {\n return {\n type: UPDATE_RESOLVED_SUBTREE,\n payload: { path, value }\n }\n}\n\nexport const invalidateResolvedSubtreeCache = () => {\n return {\n type: UPDATE_RESOLVED_SUBTREE,\n payload: {\n path: [],\n value: Map()\n }\n }\n}\n\nexport const validateParams = ( payload, isOAS3 ) =>{\n return {\n type: VALIDATE_PARAMS,\n payload:{\n pathMethod: payload,\n isOAS3\n }\n }\n}\n\nexport const updateEmptyParamInclusion = ( pathMethod, paramName, paramIn, includeEmptyValue ) =>{\n return {\n type: UPDATE_EMPTY_PARAM_INCLUSION,\n payload:{\n pathMethod,\n paramName,\n paramIn,\n includeEmptyValue\n }\n }\n}\n\nexport function clearValidateParams( payload ){\n return {\n type: CLEAR_VALIDATE_PARAMS,\n payload:{ pathMethod: payload }\n }\n}\n\nexport function changeConsumesValue(path, value) {\n return {\n type: UPDATE_OPERATION_META_VALUE,\n payload:{ path, value, key: \"consumes_value\" }\n }\n}\n\nexport function changeProducesValue(path, value) {\n return {\n type: UPDATE_OPERATION_META_VALUE,\n payload:{ path, value, key: \"produces_value\" }\n }\n}\n\nexport const setResponse = ( path, method, res ) => {\n return {\n payload: { path, method, res },\n type: SET_RESPONSE\n }\n}\n\nexport const setRequest = ( path, method, req ) => {\n return {\n payload: { path, method, req },\n type: SET_REQUEST\n }\n}\n\nexport const setMutatedRequest = ( path, method, req ) => {\n return {\n payload: { path, method, req },\n type: SET_MUTATED_REQUEST\n }\n}\n\n// This is for debugging, remove this comment if you depend on this action\nexport const logRequest = (req) => {\n return {\n payload: req,\n type: LOG_REQUEST\n }\n}\n\n// Actually fire the request via fn.execute\n// (For debugging) and ease of testing\nexport const executeRequest = (req) =>\n ({fn, specActions, specSelectors, getConfigs, oas3Selectors}) => {\n let { pathName, method, operation } = req\n let { requestInterceptor, responseInterceptor } = getConfigs()\n\n\n let op = operation.toJS()\n\n // ensure that explicitly-included params are in the request\n\n if (operation && operation.get(\"parameters\")) {\n operation.get(\"parameters\")\n .filter(param => param && param.get(\"allowEmptyValue\") === true)\n .forEach(param => {\n if (specSelectors.parameterInclusionSettingFor([pathName, method], param.get(\"name\"), param.get(\"in\"))) {\n req.parameters = req.parameters || {}\n const paramValue = paramToValue(param, req.parameters)\n\n // if the value is falsy or an empty Immutable iterable...\n if(!paramValue || (paramValue && paramValue.size === 0)) {\n // set it to empty string, so Swagger Client will treat it as\n // present but empty.\n req.parameters[param.get(\"name\")] = \"\"\n }\n }\n })\n }\n\n // if url is relative, parseUrl makes it absolute by inferring from `window.location`\n req.contextUrl = parseUrl(specSelectors.url()).toString()\n\n if(op && op.operationId) {\n req.operationId = op.operationId\n } else if(op && pathName && method) {\n req.operationId = fn.opId(op, pathName, method)\n }\n\n if(specSelectors.isOAS3()) {\n const namespace = `${pathName}:${method}`\n\n req.server = oas3Selectors.selectedServer(namespace) || oas3Selectors.selectedServer()\n\n const namespaceVariables = oas3Selectors.serverVariables({\n server: req.server,\n namespace\n }).toJS()\n const globalVariables = oas3Selectors.serverVariables({ server: req.server }).toJS()\n\n req.serverVariables = Object.keys(namespaceVariables).length ? namespaceVariables : globalVariables\n\n req.requestContentType = oas3Selectors.requestContentType(pathName, method)\n req.responseContentType = oas3Selectors.responseContentType(pathName, method) || \"*/*\"\n const requestBody = oas3Selectors.requestBodyValue(pathName, method)\n const requestBodyInclusionSetting = oas3Selectors.requestBodyInclusionSetting(pathName, method)\n\n if(requestBody && requestBody.toJS) {\n req.requestBody = requestBody\n .map(\n (val) => {\n if (Map.isMap(val)) {\n return val.get(\"value\")\n }\n return val\n }\n )\n .filter(\n (value, key) => (Array.isArray(value)\n ? value.length !== 0\n : !isEmptyValue(value)\n ) || requestBodyInclusionSetting.get(key)\n )\n .toJS()\n } else {\n req.requestBody = requestBody\n }\n }\n\n let parsedRequest = Object.assign({}, req)\n parsedRequest = fn.buildRequest(parsedRequest)\n\n specActions.setRequest(req.pathName, req.method, parsedRequest)\n\n let requestInterceptorWrapper = async (r) => {\n let mutatedRequest = await requestInterceptor.apply(this, [r])\n let parsedMutatedRequest = Object.assign({}, mutatedRequest)\n specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)\n return mutatedRequest\n }\n\n req.requestInterceptor = requestInterceptorWrapper\n req.responseInterceptor = responseInterceptor\n\n // track duration of request\n const startTime = Date.now()\n\n\n return fn.execute(req)\n .then( res => {\n res.duration = Date.now() - startTime\n specActions.setResponse(req.pathName, req.method, res)\n } )\n .catch(\n err => {\n // console.error(err)\n if(err.message === \"Failed to fetch\") {\n err.name = \"\"\n err.message = \"**Failed to fetch.** \\n**Possible Reasons:** \\n - CORS \\n - Network Failure \\n - URL scheme must be \\\"http\\\" or \\\"https\\\" for CORS request.\"\n }\n specActions.setResponse(req.pathName, req.method, {\n error: true, err: serializeError(err)\n })\n }\n )\n }\n\n\n// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat\nexport const execute = ( { path, method, ...extras }={} ) => (system) => {\n let { fn:{fetch}, specSelectors, specActions } = system\n let spec = specSelectors.specJsonWithResolvedSubtrees().toJS()\n let scheme = specSelectors.operationScheme(path, method)\n let { requestContentType, responseContentType } = specSelectors.contentTypeValues([path, method]).toJS()\n let isXml = /xml/i.test(requestContentType)\n let parameters = specSelectors.parameterValues([path, method], isXml).toJS()\n\n return specActions.executeRequest({\n ...extras,\n fetch,\n spec,\n pathName: path,\n method, parameters,\n requestContentType,\n scheme,\n responseContentType\n })\n}\n\nexport function clearResponse (path, method) {\n return {\n type: CLEAR_RESPONSE,\n payload:{ path, method }\n }\n}\n\nexport function clearRequest (path, method) {\n return {\n type: CLEAR_REQUEST,\n payload:{ path, method }\n }\n}\n\nexport function setScheme (scheme, path, method) {\n return {\n type: SET_SCHEME,\n payload: { scheme, path, method }\n }\n}\n","import reducers from \"./reducers\"\nimport * as actions from \"./actions\"\nimport * as selectors from \"./selectors\"\nimport * as wrapActions from \"./wrap-actions\"\n\nexport default function() {\n return {\n statePlugins: {\n spec: {\n wrapActions,\n reducers,\n actions,\n selectors\n }\n }\n }\n}\n","import { fromJS, List } from \"immutable\"\nimport { fromJSOrdered, validateParam, paramToValue } from \"core/utils\"\nimport win from \"../../window\"\n\n// selector-in-reducer is suboptimal, but `operationWithMeta` is more of a helper\nimport {\n specJsonWithResolvedSubtrees,\n parameterValues,\n parameterInclusionSettingFor,\n} from \"./selectors\"\n\nimport {\n UPDATE_SPEC,\n UPDATE_URL,\n UPDATE_JSON,\n UPDATE_PARAM,\n UPDATE_EMPTY_PARAM_INCLUSION,\n VALIDATE_PARAMS,\n SET_RESPONSE,\n SET_REQUEST,\n SET_MUTATED_REQUEST,\n UPDATE_RESOLVED,\n UPDATE_RESOLVED_SUBTREE,\n UPDATE_OPERATION_META_VALUE,\n CLEAR_RESPONSE,\n CLEAR_REQUEST,\n CLEAR_VALIDATE_PARAMS,\n SET_SCHEME\n} from \"./actions\"\nimport { paramToIdentifier } from \"../../utils\"\n\nexport default {\n\n [UPDATE_SPEC]: (state, action) => {\n return (typeof action.payload === \"string\")\n ? state.set(\"spec\", action.payload)\n : state\n },\n\n [UPDATE_URL]: (state, action) => {\n return state.set(\"url\", action.payload+\"\")\n },\n\n [UPDATE_JSON]: (state, action) => {\n return state.set(\"json\", fromJSOrdered(action.payload))\n },\n\n [UPDATE_RESOLVED]: (state, action) => {\n return state.setIn([\"resolved\"], fromJSOrdered(action.payload))\n },\n\n [UPDATE_RESOLVED_SUBTREE]: (state, action) => {\n const { value, path } = action.payload\n return state.setIn([\"resolvedSubtrees\", ...path], fromJSOrdered(value))\n },\n\n [UPDATE_PARAM]: ( state, {payload} ) => {\n let { path: pathMethod, paramName, paramIn, param, value, isXml } = payload\n\n let paramKey = param ? paramToIdentifier(param) : `${paramIn}.${paramName}`\n\n const valueKey = isXml ? \"value_xml\" : \"value\"\n\n return state.setIn(\n [\"meta\", \"paths\", ...pathMethod, \"parameters\", paramKey, valueKey],\n value\n )\n },\n\n [UPDATE_EMPTY_PARAM_INCLUSION]: ( state, {payload} ) => {\n let { pathMethod, paramName, paramIn, includeEmptyValue } = payload\n\n if(!paramName || !paramIn) {\n console.warn(\"Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey.\")\n return state\n }\n\n const paramKey = `${paramIn}.${paramName}`\n\n return state.setIn(\n [\"meta\", \"paths\", ...pathMethod, \"parameter_inclusions\", paramKey],\n includeEmptyValue\n )\n },\n\n [VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {\n const op = specJsonWithResolvedSubtrees(state).getIn([\"paths\", ...pathMethod])\n const paramValues = parameterValues(state, pathMethod).toJS()\n\n return state.updateIn([\"meta\", \"paths\", ...pathMethod, \"parameters\"], fromJS({}), paramMeta => {\n return op.get(\"parameters\", List()).reduce((res, param) => {\n const value = paramToValue(param, paramValues)\n const isEmptyValueIncluded = parameterInclusionSettingFor(state, pathMethod, param.get(\"name\"), param.get(\"in\"))\n const errors = validateParam(param, value, {\n bypassRequiredCheck: isEmptyValueIncluded,\n isOAS3,\n })\n return res.setIn([paramToIdentifier(param), \"errors\"], fromJS(errors))\n }, paramMeta)\n })\n },\n [CLEAR_VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {\n return state.updateIn( [ \"meta\", \"paths\", ...pathMethod, \"parameters\" ], fromJS([]), parameters => {\n return parameters.map(param => param.set(\"errors\", fromJS([])))\n })\n },\n\n [SET_RESPONSE]: (state, { payload: { res, path, method } } ) =>{\n let result\n if ( res.error ) {\n result = Object.assign({\n error: true,\n name: res.err.name,\n message: res.err.message,\n statusCode: res.err.statusCode\n }, res.err.response)\n } else {\n result = res\n }\n\n // Ensure headers\n result.headers = result.headers || {}\n\n let newState = state.setIn( [ \"responses\", path, method ], fromJSOrdered(result) )\n\n // ImmutableJS messes up Blob. Needs to reset its value.\n if (win.Blob && res.data instanceof win.Blob) {\n newState = newState.setIn( [ \"responses\", path, method, \"text\" ], res.data)\n }\n return newState\n },\n\n [SET_REQUEST]: (state, { payload: { req, path, method } } ) =>{\n return state.setIn( [ \"requests\", path, method ], fromJSOrdered(req))\n },\n\n [SET_MUTATED_REQUEST]: (state, { payload: { req, path, method } } ) =>{\n return state.setIn( [ \"mutatedRequests\", path, method ], fromJSOrdered(req))\n },\n\n [UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {\n // path is a pathMethod tuple... can't change the name now.\n let operationPath = [\"paths\", ...path]\n let metaPath = [\"meta\", \"paths\", ...path]\n\n if(\n !state.getIn([\"json\", ...operationPath])\n && !state.getIn([\"resolved\", ...operationPath])\n && !state.getIn([\"resolvedSubtrees\", ...operationPath])\n ) {\n // do nothing if the operation does not exist\n return state\n }\n\n return state.setIn([...metaPath, key], fromJS(value))\n },\n\n [CLEAR_RESPONSE]: (state, { payload: { path, method } } ) =>{\n return state.deleteIn( [ \"responses\", path, method ])\n },\n\n [CLEAR_REQUEST]: (state, { payload: { path, method } } ) =>{\n return state.deleteIn( [ \"requests\", path, method ])\n },\n\n [SET_SCHEME]: (state, { payload: { scheme, path, method } } ) =>{\n if ( path && method ) {\n return state.setIn( [ \"scheme\", path, method ], scheme)\n }\n\n if (!path && !method) {\n return state.setIn( [ \"scheme\", \"_defaultScheme\" ], scheme)\n }\n\n }\n\n}\n","import { createSelector } from \"reselect\"\nimport { sorters } from \"core/utils\"\nimport { fromJS, Set, Map, OrderedMap, List } from \"immutable\"\nimport { paramToIdentifier } from \"../../utils\"\n\nconst DEFAULT_TAG = \"default\"\n\nconst OPERATION_METHODS = [\n \"get\", \"put\", \"post\", \"delete\", \"options\", \"head\", \"patch\", \"trace\"\n]\n\nconst state = state => {\n return state || Map()\n}\n\nexport const lastError = createSelector(\n state,\n spec => spec.get(\"lastError\")\n)\n\nexport const url = createSelector(\n state,\n spec => spec.get(\"url\")\n)\n\nexport const specStr = createSelector(\n state,\n spec => spec.get(\"spec\") || \"\"\n)\n\nexport const specSource = createSelector(\n state,\n spec => spec.get(\"specSource\") || \"not-editor\"\n)\n\nexport const specJson = createSelector(\n state,\n spec => spec.get(\"json\", Map())\n)\n\nexport const specResolved = createSelector(\n state,\n spec => spec.get(\"resolved\", Map())\n)\n\nexport const specResolvedSubtree = (state, path) => {\n return state.getIn([\"resolvedSubtrees\", ...path], undefined)\n}\n\nconst mergerFn = (oldVal, newVal) => {\n if(Map.isMap(oldVal) && Map.isMap(newVal)) {\n if(newVal.get(\"$$ref\")) {\n // resolver artifacts indicated that this key was directly resolved\n // so we should drop the old value entirely\n return newVal\n }\n\n return OrderedMap().mergeWith(\n mergerFn,\n oldVal,\n newVal\n )\n }\n\n return newVal\n}\n\nexport const specJsonWithResolvedSubtrees = createSelector(\n state,\n spec => OrderedMap().mergeWith(\n mergerFn,\n spec.get(\"json\"),\n spec.get(\"resolvedSubtrees\")\n )\n)\n\n// Default Spec ( as an object )\nexport const spec = state => {\n let res = specJson(state)\n return res\n}\n\nexport const isOAS3 = createSelector(\n // isOAS3 is stubbed out here to work around an issue with injecting more selectors\n // in the OAS3 plugin, and to ensure that the function is always available.\n // It's not perfect, but our hybrid (core+plugin code) implementation for OAS3\n // needs this. //KS\n spec,\n\t() => false\n)\n\nexport const info = createSelector(\n spec,\n\tspec => returnSelfOrNewMap(spec && spec.get(\"info\"))\n)\n\nexport const externalDocs = createSelector(\n spec,\n\tspec => returnSelfOrNewMap(spec && spec.get(\"externalDocs\"))\n)\n\nexport const version = createSelector(\n\tinfo,\n\tinfo => info && info.get(\"version\")\n)\n\nexport const semver = createSelector(\n\tversion,\n\tversion => /v?([0-9]*)\\.([0-9]*)\\.([0-9]*)/i.exec(version).slice(1)\n)\n\nexport const paths = createSelector(\n\tspecJsonWithResolvedSubtrees,\n\tspec => spec.get(\"paths\")\n)\n\nexport const operations = createSelector(\n paths,\n paths => {\n if(!paths || paths.size < 1)\n return List()\n\n let list = List()\n\n if(!paths || !paths.forEach) {\n return List()\n }\n\n paths.forEach((path, pathName) => {\n if(!path || !path.forEach) {\n return {}\n }\n path.forEach((operation, method) => {\n if(OPERATION_METHODS.indexOf(method) < 0) {\n return\n }\n list = list.push(fromJS({\n path: pathName,\n method,\n operation,\n id: `${method}-${pathName}`\n }))\n })\n })\n\n return list\n }\n)\n\nexport const consumes = createSelector(\n spec,\n spec => Set(spec.get(\"consumes\"))\n)\n\nexport const produces = createSelector(\n spec,\n spec => Set(spec.get(\"produces\"))\n)\n\nexport const security = createSelector(\n spec,\n spec => spec.get(\"security\", List())\n)\n\nexport const securityDefinitions = createSelector(\n spec,\n spec => spec.get(\"securityDefinitions\")\n)\n\n\nexport const findDefinition = ( state, name ) => {\n const resolvedRes = state.getIn([\"resolvedSubtrees\", \"definitions\", name], null)\n const unresolvedRes = state.getIn([\"json\", \"definitions\", name], null)\n return resolvedRes || unresolvedRes || null\n}\n\nexport const definitions = createSelector(\n spec,\n spec => {\n const res = spec.get(\"definitions\")\n return Map.isMap(res) ? res : Map()\n }\n)\n\nexport const basePath = createSelector(\n spec,\n spec => spec.get(\"basePath\")\n)\n\nexport const host = createSelector(\n spec,\n spec => spec.get(\"host\")\n)\n\nexport const schemes = createSelector(\n spec,\n spec => spec.get(\"schemes\", Map())\n)\n\nexport const operationsWithRootInherited = createSelector(\n operations,\n consumes,\n produces,\n (operations, consumes, produces) => {\n return operations.map( ops => ops.update(\"operation\", op => {\n if(op) {\n if(!Map.isMap(op)) { return }\n return op.withMutations( op => {\n if ( !op.get(\"consumes\") ) {\n op.update(\"consumes\", a => Set(a).merge(consumes))\n }\n if ( !op.get(\"produces\") ) {\n op.update(\"produces\", a => Set(a).merge(produces))\n }\n return op\n })\n } else {\n // return something with Immutable methods\n return Map()\n }\n\n }))\n }\n)\n\nexport const tags = createSelector(\n spec,\n json => {\n const tags = json.get(\"tags\", List())\n return List.isList(tags) ? tags.filter(tag => Map.isMap(tag)) : List()\n }\n)\n\nexport const tagDetails = (state, tag) => {\n let currentTags = tags(state) || List()\n return currentTags.filter(Map.isMap).find(t => t.get(\"name\") === tag, Map())\n}\n\nexport const operationsWithTags = createSelector(\n operationsWithRootInherited,\n tags,\n (operations, tags) => {\n return operations.reduce( (taggedMap, op) => {\n let tags = Set(op.getIn([\"operation\",\"tags\"]))\n if(tags.count() < 1)\n return taggedMap.update(DEFAULT_TAG, List(), ar => ar.push(op))\n return tags.reduce( (res, tag) => res.update(tag, List(), (ar) => ar.push(op)), taggedMap )\n }, tags.reduce( (taggedMap, tag) => {\n return taggedMap.set(tag.get(\"name\"), List())\n } , OrderedMap()))\n }\n)\n\nexport const taggedOperations = (state) => ({ getConfigs }) => {\n let { tagsSorter, operationsSorter } = getConfigs()\n return operationsWithTags(state)\n .sortBy(\n (val, key) => key, // get the name of the tag to be passed to the sorter\n (tagA, tagB) => {\n let sortFn = (typeof tagsSorter === \"function\" ? tagsSorter : sorters.tagsSorter[ tagsSorter ])\n return (!sortFn ? null : sortFn(tagA, tagB))\n }\n )\n .map((ops, tag) => {\n let sortFn = (typeof operationsSorter === \"function\" ? operationsSorter : sorters.operationsSorter[ operationsSorter ])\n let operations = (!sortFn ? ops : ops.sort(sortFn))\n\n return Map({ tagDetails: tagDetails(state, tag), operations: operations })\n })\n}\n\nexport const responses = createSelector(\n state,\n state => state.get( \"responses\", Map() )\n)\n\nexport const requests = createSelector(\n state,\n state => state.get( \"requests\", Map() )\n)\n\nexport const mutatedRequests = createSelector(\n state,\n state => state.get( \"mutatedRequests\", Map() )\n)\n\nexport const responseFor = (state, path, method) => {\n return responses(state).getIn([path, method], null)\n}\n\nexport const requestFor = (state, path, method) => {\n return requests(state).getIn([path, method], null)\n}\n\nexport const mutatedRequestFor = (state, path, method) => {\n return mutatedRequests(state).getIn([path, method], null)\n}\n\nexport const allowTryItOutFor = () => {\n // This is just a hook for now.\n return true\n}\n\nexport const parameterWithMetaByIdentity = (state, pathMethod, param) => {\n const opParams = specJsonWithResolvedSubtrees(state).getIn([\"paths\", ...pathMethod, \"parameters\"], OrderedMap())\n const metaParams = state.getIn([\"meta\", \"paths\", ...pathMethod, \"parameters\"], OrderedMap())\n\n const mergedParams = opParams.map((currentParam) => {\n const inNameKeyedMeta = metaParams.get(`${param.get(\"in\")}.${param.get(\"name\")}`)\n const hashKeyedMeta = metaParams.get(`${param.get(\"in\")}.${param.get(\"name\")}.hash-${param.hashCode()}`)\n return OrderedMap().merge(\n currentParam,\n inNameKeyedMeta,\n hashKeyedMeta\n )\n })\n return mergedParams.find(curr => curr.get(\"in\") === param.get(\"in\") && curr.get(\"name\") === param.get(\"name\"), OrderedMap())\n}\n\nexport const parameterInclusionSettingFor = (state, pathMethod, paramName, paramIn) => {\n const paramKey = `${paramIn}.${paramName}`\n return state.getIn([\"meta\", \"paths\", ...pathMethod, \"parameter_inclusions\", paramKey], false)\n}\n\n\nexport const parameterWithMeta = (state, pathMethod, paramName, paramIn) => {\n const opParams = specJsonWithResolvedSubtrees(state).getIn([\"paths\", ...pathMethod, \"parameters\"], OrderedMap())\n const currentParam = opParams.find(param => param.get(\"in\") === paramIn && param.get(\"name\") === paramName, OrderedMap())\n return parameterWithMetaByIdentity(state, pathMethod, currentParam)\n}\n\nexport const operationWithMeta = (state, path, method) => {\n const op = specJsonWithResolvedSubtrees(state).getIn([\"paths\", path, method], OrderedMap())\n const meta = state.getIn([\"meta\", \"paths\", path, method], OrderedMap())\n\n const mergedParams = op.get(\"parameters\", List()).map((param) => {\n return parameterWithMetaByIdentity(state, [path, method], param)\n })\n\n return OrderedMap()\n .merge(op, meta)\n .set(\"parameters\", mergedParams)\n}\n\n// Get the parameter value by parameter name\nexport function getParameter(state, pathMethod, name, inType) {\n pathMethod = pathMethod || []\n let params = state.getIn([\"meta\", \"paths\", ...pathMethod, \"parameters\"], fromJS([]))\n return params.find( (p) => {\n return Map.isMap(p) && p.get(\"name\") === name && p.get(\"in\") === inType\n }) || Map() // Always return a map\n}\n\nexport const hasHost = createSelector(\n spec,\n spec => {\n const host = spec.get(\"host\")\n return typeof host === \"string\" && host.length > 0 && host[0] !== \"/\"\n }\n)\n\n// Get the parameter values, that the user filled out\nexport function parameterValues(state, pathMethod, isXml) {\n pathMethod = pathMethod || []\n let paramValues = operationWithMeta(state, ...pathMethod).get(\"parameters\", List())\n return paramValues.reduce( (hash, p) => {\n let value = isXml && p.get(\"in\") === \"body\" ? p.get(\"value_xml\") : p.get(\"value\")\n return hash.set(paramToIdentifier(p, { allowHashes: false }), value)\n }, fromJS({}))\n}\n\n// True if any parameter includes `in: ?`\nexport function parametersIncludeIn(parameters, inValue=\"\") {\n if(List.isList(parameters)) {\n return parameters.some( p => Map.isMap(p) && p.get(\"in\") === inValue )\n }\n}\n\n// True if any parameter includes `type: ?`\nexport function parametersIncludeType(parameters, typeValue=\"\") {\n if(List.isList(parameters)) {\n return parameters.some( p => Map.isMap(p) && p.get(\"type\") === typeValue )\n }\n}\n\n// Get the consumes/produces value that the user selected\nexport function contentTypeValues(state, pathMethod) {\n pathMethod = pathMethod || []\n let op = specJsonWithResolvedSubtrees(state).getIn([\"paths\", ...pathMethod], fromJS({}))\n let meta = state.getIn([\"meta\", \"paths\", ...pathMethod], fromJS({}))\n let producesValue = currentProducesFor(state, pathMethod)\n\n const parameters = op.get(\"parameters\") || new List()\n\n const requestContentType = (\n meta.get(\"consumes_value\") ? meta.get(\"consumes_value\")\n : parametersIncludeType(parameters, \"file\") ? \"multipart/form-data\"\n : parametersIncludeType(parameters, \"formData\") ? \"application/x-www-form-urlencoded\"\n : undefined\n )\n\n return fromJS({\n requestContentType,\n responseContentType: producesValue\n })\n}\n\n// Get the currently selected produces value for an operation\nexport function currentProducesFor(state, pathMethod) {\n pathMethod = pathMethod || []\n\n const operation = specJsonWithResolvedSubtrees(state).getIn([ \"paths\", ...pathMethod], null)\n\n if(operation === null) {\n // return nothing if the operation does not exist\n return\n }\n\n const currentProducesValue = state.getIn([\"meta\", \"paths\", ...pathMethod, \"produces_value\"], null)\n const firstProducesArrayItem = operation.getIn([\"produces\", 0], null)\n\n return currentProducesValue || firstProducesArrayItem || \"application/json\"\n\n}\n\n// Get the produces options for an operation\nexport function producesOptionsFor(state, pathMethod) {\n pathMethod = pathMethod || []\n\n const spec = specJsonWithResolvedSubtrees(state)\n const operation = spec.getIn([ \"paths\", ...pathMethod], null)\n\n if(operation === null) {\n // return nothing if the operation does not exist\n return\n }\n\n const [path] = pathMethod\n\n const operationProduces = operation.get(\"produces\", null)\n const pathItemProduces = spec.getIn([\"paths\", path, \"produces\"], null)\n const globalProduces = spec.getIn([\"produces\"], null)\n\n return operationProduces || pathItemProduces || globalProduces\n}\n\n// Get the consumes options for an operation\nexport function consumesOptionsFor(state, pathMethod) {\n pathMethod = pathMethod || []\n\n const spec = specJsonWithResolvedSubtrees(state)\n const operation = spec.getIn([\"paths\", ...pathMethod], null)\n\n if (operation === null) {\n // return nothing if the operation does not exist\n return\n }\n\n const [path] = pathMethod\n\n const operationConsumes = operation.get(\"consumes\", null)\n const pathItemConsumes = spec.getIn([\"paths\", path, \"consumes\"], null)\n const globalConsumes = spec.getIn([\"consumes\"], null)\n\n return operationConsumes || pathItemConsumes || globalConsumes\n}\n\nexport const operationScheme = ( state, path, method ) => {\n let url = state.get(\"url\")\n let matchResult = url.match(/^([a-z][a-z0-9+\\-.]*):/)\n let urlScheme = Array.isArray(matchResult) ? matchResult[1] : null\n\n return state.getIn([\"scheme\", path, method]) || state.getIn([\"scheme\", \"_defaultScheme\"]) || urlScheme || \"\"\n}\n\nexport const canExecuteScheme = ( state, path, method ) => {\n return [\"http\", \"https\"].indexOf(operationScheme(state, path, method)) > -1\n}\n\nexport const validateBeforeExecute = ( state, pathMethod ) => {\n pathMethod = pathMethod || []\n let paramValues = state.getIn([\"meta\", \"paths\", ...pathMethod, \"parameters\"], fromJS([]))\n let isValid = true\n\n paramValues.forEach( (p) => {\n let errors = p.get(\"errors\")\n if ( errors && errors.count() ) {\n isValid = false\n }\n })\n\n return isValid\n}\n\nexport const getOAS3RequiredRequestBodyContentType = (state, pathMethod) => {\n let requiredObj = {\n requestBody: false,\n requestContentType: {}\n }\n let requestBody = state.getIn([\"resolvedSubtrees\", \"paths\", ...pathMethod, \"requestBody\"], fromJS([]))\n if (requestBody.size < 1) {\n return requiredObj\n }\n if (requestBody.getIn([\"required\"])) {\n requiredObj.requestBody = requestBody.getIn([\"required\"])\n }\n requestBody.getIn([\"content\"]).entrySeq().forEach((contentType) => { // e.g application/json\n const key = contentType[0]\n if (contentType[1].getIn([\"schema\", \"required\"])) {\n const val = contentType[1].getIn([\"schema\", \"required\"]).toJS()\n requiredObj.requestContentType[key] = val\n }\n })\n return requiredObj\n}\n\nexport const isMediaTypeSchemaPropertiesEqual = ( state, pathMethod, currentMediaType, targetMediaType) => {\n if((currentMediaType || targetMediaType) && currentMediaType === targetMediaType ) {\n return true\n }\n let requestBodyContent = state.getIn([\"resolvedSubtrees\", \"paths\", ...pathMethod, \"requestBody\", \"content\"], fromJS([]))\n if (requestBodyContent.size < 2 || !currentMediaType || !targetMediaType) {\n // nothing to compare\n return false\n }\n let currentMediaTypeSchemaProperties = requestBodyContent.getIn([currentMediaType, \"schema\", \"properties\"], fromJS([]))\n let targetMediaTypeSchemaProperties = requestBodyContent.getIn([targetMediaType, \"schema\", \"properties\"], fromJS([]))\n return !!currentMediaTypeSchemaProperties.equals(targetMediaTypeSchemaProperties)\n}\n\nfunction returnSelfOrNewMap(obj) {\n // returns obj if obj is an Immutable map, else returns a new Map\n return Map.isMap(obj) ? obj : new Map()\n}\n","import get from \"lodash/get\"\n\nexport const updateSpec = (ori, {specActions}) => (...args) => {\n ori(...args)\n specActions.parseToJson(...args)\n}\n\nexport const updateJsonSpec = (ori, {specActions}) => (...args) => {\n ori(...args)\n\n specActions.invalidateResolvedSubtreeCache()\n\n // Trigger resolution of any path-level $refs.\n const [json] = args\n const pathItems = get(json, [\"paths\"]) || {}\n const pathItemKeys = Object.keys(pathItems)\n\n pathItemKeys.forEach(k => {\n const val = get(pathItems, [k])\n\n if(val.$ref) {\n specActions.requestResolvedSubtree([\"paths\", k])\n }\n })\n\n // Trigger resolution of any securitySchemes-level $refs.\n specActions.requestResolvedSubtree([\"components\", \"securitySchemes\"])\n}\n\n// Log the request ( just for debugging, shouldn't affect prod )\nexport const executeRequest = (ori, { specActions }) => (req) => {\n specActions.logRequest(req)\n return ori(req)\n}\n\nexport const validateParams = (ori, { specSelectors }) => (req) => {\n return ori(req, specSelectors.isOAS3())\n}\n","export const loaded = (ori, system) => (...args) => {\n ori(...args)\n const value = system.getConfigs().withCredentials\n \n if(value !== undefined) {\n system.fn.fetch.withCredentials = typeof value === \"string\" ? (value === \"true\") : !!value\n }\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_resolver_f879c638__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"buildRequest\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_execute_d486d3d6__.buildRequest, [\"execute\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_execute_d486d3d6__.execute });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_http_69655560__[\"default\"], [\"makeHttp\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_http_69655560__.makeHttp, [\"serializeRes\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_http_69655560__.serializeRes });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_subtree_resolver_741cb9d9__[\"default\"] });","import resolve from \"swagger-client/es/resolver\"\nimport { execute, buildRequest } from \"swagger-client/es/execute\"\nimport Http, { makeHttp, serializeRes } from \"swagger-client/es/http\"\nimport resolveSubtree from \"swagger-client/es/subtree-resolver\"\nimport { opId } from \"swagger-client/es/helpers\"\nimport { loaded } from \"./configs-wrap-actions\"\n\nexport default function({ configs, getConfigs }) {\n return {\n fn: {\n fetch: makeHttp(Http, configs.preFetch, configs.postFetch),\n buildRequest,\n execute,\n resolve,\n resolveSubtree: (obj, path, opts, ...rest) => {\n if(opts === undefined) {\n const freshConfigs = getConfigs()\n opts = {\n modelPropertyMacro: freshConfigs.modelPropertyMacro,\n parameterMacro: freshConfigs.parameterMacro,\n requestInterceptor: freshConfigs.requestInterceptor,\n responseInterceptor: freshConfigs.responseInterceptor\n }\n }\n\n return resolveSubtree(obj, path, opts, ...rest)\n },\n serializeRes,\n opId\n },\n statePlugins: {\n configs: {\n wrapActions: {\n loaded,\n }\n }\n },\n }\n}\n","import { shallowEqualKeys } from \"core/utils\"\n\nexport default function() {\n return {\n fn: { shallowEqualKeys }\n }\n}\n","export const getDisplayName = (WrappedComponent) => WrappedComponent.displayName || WrappedComponent.name || \"Component\"\n","import { memoize } from \"core/utils\"\n\nimport { getComponent, render, withMappedContainer } from \"./root-injects\"\nimport { getDisplayName } from \"./fn\"\nimport memoizeN from \"../../../helpers/memoizeN\"\n\nconst memoizeForGetComponent = (fn) => {\n const resolver = (...args) => JSON.stringify(args)\n return memoize(fn, resolver)\n}\n\nconst memoizeForWithMappedContainer = (fn) => {\n const resolver = (...args) => args\n return memoizeN(fn, resolver)\n}\n\nconst viewPlugin = ({getComponents, getStore, getSystem}) => {\n // getComponent should be passed into makeMappedContainer, _already_ memoized... otherwise we have a big performance hit ( think, really big )\n const memGetComponent = memoizeForGetComponent(getComponent(getSystem, getStore, getComponents))\n const memMakeMappedContainer = memoizeForWithMappedContainer(withMappedContainer(getSystem, getStore, memGetComponent))\n\n return {\n rootInjects: {\n getComponent: memGetComponent,\n makeMappedContainer: memMakeMappedContainer,\n render: render(getSystem, getStore, getComponent, getComponents),\n },\n fn: {\n getDisplayName,\n },\n }\n}\n\nexport default viewPlugin\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_dom_7dac9eee__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"Provider\"]: () => __WEBPACK_EXTERNAL_MODULE_react_redux_87be03b0__.Provider, [\"connect\"]: () => __WEBPACK_EXTERNAL_MODULE_react_redux_87be03b0__.connect });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_omit_d930e0f3__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_identity_75ffe4a7__[\"default\"] });","import React, { Component } from \"react\"\nimport ReactDOM from \"react-dom\"\nimport { compose } from \"redux\"\nimport { connect, Provider } from \"react-redux\"\nimport omit from \"lodash/omit\"\nimport identity from \"lodash/identity\"\n\nconst withSystem = (getSystem) => (WrappedComponent) => {\n const { fn } = getSystem()\n\n class WithSystem extends Component {\n render() {\n return \n }\n }\n WithSystem.displayName = `WithSystem(${fn.getDisplayName(WrappedComponent)})`\n return WithSystem\n}\n\nconst withRoot = (getSystem, reduxStore) => (WrappedComponent) => {\n const { fn } = getSystem()\n\n class WithRoot extends Component {\n render() {\n return (\n \n \n \n )\n }\n }\n WithRoot.displayName = `WithRoot(${fn.getDisplayName(WrappedComponent)})`\n return WithRoot\n}\n\nconst withConnect = (getSystem, WrappedComponent, reduxStore) => {\n const mapStateToProps = (state, ownProps) => {\n const props = {...ownProps, ...getSystem()}\n const customMapStateToProps = WrappedComponent.prototype?.mapStateToProps || (state => ({state}))\n return customMapStateToProps(state, props)\n }\n\n return compose(\n reduxStore ? withRoot(getSystem, reduxStore) : identity,\n connect(mapStateToProps),\n withSystem(getSystem),\n )(WrappedComponent)\n}\n\nconst handleProps = (getSystem, mapping, props, oldProps) => {\n for (const prop in mapping) {\n const fn = mapping[prop]\n\n if (typeof fn === \"function\") {\n fn(props[prop], oldProps[prop], getSystem())\n }\n }\n}\n\nexport const withMappedContainer = (getSystem, getStore, memGetComponent) => (componentName, mapping) => {\n const { fn } = getSystem()\n const WrappedComponent = memGetComponent(componentName, \"root\")\n\n class WithMappedContainer extends Component {\n constructor(props, context) {\n super(props, context)\n handleProps(getSystem, mapping, props, {})\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n handleProps(getSystem, mapping, nextProps, this.props)\n }\n\n render() {\n const cleanProps = omit(this.props, mapping ? Object.keys(mapping) : [])\n return \n }\n }\n WithMappedContainer.displayName = `WithMappedContainer(${fn.getDisplayName(WrappedComponent)})`\n return WithMappedContainer\n}\n\nexport const render = (getSystem, getStore, getComponent, getComponents) => (domNode) => {\n const App = getComponent(getSystem, getStore, getComponents)(\"App\", \"root\")\n ReactDOM.render(, domNode)\n}\n\nexport const getComponent = (getSystem, getStore, getComponents) => (componentName, container, config = {}) => {\n\n if (typeof componentName !== \"string\")\n throw new TypeError(\"Need a string, to fetch a component. Was given a \" + typeof componentName)\n\n // getComponent has a config object as a third, optional parameter\n // using the config object requires the presence of the second parameter, container\n // e.g. getComponent(\"JsonSchema_string_whatever\", false, { failSilently: true })\n const component = getComponents(componentName)\n\n if (!component) {\n if (!config.failSilently) {\n getSystem().log.warn(\"Could not find component:\", componentName)\n }\n return null\n }\n\n if(!container) {\n return component\n }\n\n if(container === \"root\") {\n return withConnect(getSystem, component, getStore())\n }\n\n // container == truthy\n return withConnect(getSystem, component)\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_light_746e1958__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_javascript_e22911f7__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_json_b876afc5__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_xml_a81c807b__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_bash_1621c621__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_yaml_02838f34__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_http_4e924b23__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_languages_hljs_powershell_d51eb4f6__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_styles_hljs_agate_99a46aa2__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_styles_hljs_arta_570691fc__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_styles_hljs_monokai_2529bafb__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_styles_hljs_nord_5bfa1099__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_styles_hljs_obsidian_a278dd52__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_syntax_highlighter_dist_esm_styles_hljs_tomorrow_night_63765df9__[\"default\"] });","import SyntaxHighlighter from \"react-syntax-highlighter/dist/esm/light\"\nimport js from \"react-syntax-highlighter/dist/esm/languages/hljs/javascript\"\nimport json from \"react-syntax-highlighter/dist/esm/languages/hljs/json\"\nimport xml from \"react-syntax-highlighter/dist/esm/languages/hljs/xml\"\nimport bash from \"react-syntax-highlighter/dist/esm/languages/hljs/bash\"\nimport yaml from \"react-syntax-highlighter/dist/esm/languages/hljs/yaml\"\nimport http from \"react-syntax-highlighter/dist/esm/languages/hljs/http\"\nimport powershell from \"react-syntax-highlighter/dist/esm/languages/hljs/powershell\"\nimport javascript from \"react-syntax-highlighter/dist/esm/languages/hljs/javascript\"\n\nimport agate from \"react-syntax-highlighter/dist/esm/styles/hljs/agate\"\nimport arta from \"react-syntax-highlighter/dist/esm/styles/hljs/arta\"\nimport monokai from \"react-syntax-highlighter/dist/esm/styles/hljs/monokai\"\nimport nord from \"react-syntax-highlighter/dist/esm/styles/hljs/nord\"\nimport obsidian from \"react-syntax-highlighter/dist/esm/styles/hljs/obsidian\"\nimport tomorrowNight from \"react-syntax-highlighter/dist/esm/styles/hljs/tomorrow-night\"\n\nSyntaxHighlighter.registerLanguage(\"json\", json)\nSyntaxHighlighter.registerLanguage(\"js\", js)\nSyntaxHighlighter.registerLanguage(\"xml\", xml)\nSyntaxHighlighter.registerLanguage(\"yaml\", yaml)\nSyntaxHighlighter.registerLanguage(\"http\", http)\nSyntaxHighlighter.registerLanguage(\"bash\", bash)\nSyntaxHighlighter.registerLanguage(\"powershell\", powershell)\nSyntaxHighlighter.registerLanguage(\"javascript\", javascript)\n\nconst styles = {agate, arta, monokai, nord, obsidian, \"tomorrow-night\": tomorrowNight}\nexport const availableStyles = Object.keys(styles)\n\nexport const getStyle = name => {\n if (!availableStyles.includes(name)) {\n console.warn(`Request style '${name}' is not available, returning default instead`)\n return agate\n }\n return styles[name]\n}\n\nexport {SyntaxHighlighter, styles}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"sanitizeUrl\"]: () => __WEBPACK_EXTERNAL_MODULE__braintree_sanitize_url_2340607f__.sanitizeUrl });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_camelCase_81fadc19__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_upperFirst_9993ecb4__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_find_e8ecc2cb__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_some_5cd47809__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_eq_b41b823a__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_css_escape_2d301448__[\"default\"] });","/*\n ATTENTION! This file (but not the functions within) is deprecated.\n\n You should probably add a new file to `./helpers/` instead of adding a new\n function here.\n\n One-function-per-file is a better pattern than what we have here.\n\n If you're refactoring something in here, feel free to break it out to a file\n in `./helpers` if you have the time.\n*/\n\nimport Im, { fromJS, Set } from \"immutable\"\nimport { sanitizeUrl as braintreeSanitizeUrl } from \"@braintree/sanitize-url\"\nimport camelCase from \"lodash/camelCase\"\nimport upperFirst from \"lodash/upperFirst\"\nimport _memoize from \"lodash/memoize\"\nimport find from \"lodash/find\"\nimport some from \"lodash/some\"\nimport eq from \"lodash/eq\"\nimport isFunction from \"lodash/isFunction\"\nimport { memoizedSampleFromSchema, memoizedCreateXMLExample } from \"core/plugins/samples/fn\"\nimport win from \"./window\"\nimport cssEscape from \"css.escape\"\nimport getParameterSchema from \"../helpers/get-parameter-schema\"\nimport randomBytes from \"randombytes\"\nimport shaJs from \"sha.js\"\nimport YAML, { JSON_SCHEMA } from \"js-yaml\"\n\n\nconst DEFAULT_RESPONSE_KEY = \"default\"\n\nexport const isImmutable = (maybe) => Im.Iterable.isIterable(maybe)\n\nexport function objectify (thing) {\n if(!isObject(thing))\n return {}\n if(isImmutable(thing))\n return thing.toJS()\n return thing\n}\n\nexport function arrayify (thing) {\n if(!thing)\n return []\n\n if(thing.toArray)\n return thing.toArray()\n\n return normalizeArray(thing)\n}\n\nexport function fromJSOrdered(js) {\n if (isImmutable(js)) {\n return js // Can't do much here\n }\n if (js instanceof win.File) {\n return js\n }\n if (!isObject(js)) {\n return js\n }\n if (Array.isArray(js)) {\n return Im.Seq(js).map(fromJSOrdered).toList()\n }\n if (isFunction(js.entries)) {\n // handle multipart/form-data\n const objWithHashedKeys = createObjWithHashedKeys(js)\n return Im.OrderedMap(objWithHashedKeys).map(fromJSOrdered)\n }\n return Im.OrderedMap(js).map(fromJSOrdered)\n}\n\n/**\n * Convert a FormData object into plain object\n * Append a hashIdx and counter to the key name, if multiple exists\n * if single, key name = \n * if multiple, key name = \n * @example single entry for vegetable\n * fdObj.entries.vegtables: \"carrot\"\n * // returns newObj.vegetables : \"carrot\"\n * @example multiple entries for fruits[]\n * fdObj.entries.fruits[]: \"apple\"\n * // returns newObj.fruits[]_**[]1 : \"apple\"\n * fdObj.entries.fruits[]: \"banana\"\n * // returns newObj.fruits[]_**[]2 : \"banana\"\n * fdObj.entries.fruits[]: \"grape\"\n * // returns newObj.fruits[]_**[]3 : \"grape\"\n * @param {FormData} fdObj - a FormData object\n * @return {Object} - a plain object\n */\nexport function createObjWithHashedKeys (fdObj) {\n if (!isFunction(fdObj.entries)) {\n return fdObj // not a FormData object with iterable\n }\n const newObj = {}\n const hashIdx = \"_**[]\" // our internal identifier\n const trackKeys = {}\n for (let pair of fdObj.entries()) {\n if (!newObj[pair[0]] && !(trackKeys[pair[0]] && trackKeys[pair[0]].containsMultiple)) {\n newObj[pair[0]] = pair[1] // first key name: no hash required\n } else {\n if (!trackKeys[pair[0]]) {\n // initiate tracking key for multiple\n trackKeys[pair[0]] = {\n containsMultiple: true,\n length: 1\n }\n // \"reassign\" first pair to matching hashed format for multiple\n let hashedKeyFirst = `${pair[0]}${hashIdx}${trackKeys[pair[0]].length}`\n newObj[hashedKeyFirst] = newObj[pair[0]]\n // remove non-hashed key of multiple\n delete newObj[pair[0]] // first\n }\n trackKeys[pair[0]].length += 1\n let hashedKeyCurrent = `${pair[0]}${hashIdx}${trackKeys[pair[0]].length}`\n newObj[hashedKeyCurrent] = pair[1]\n }\n }\n return newObj\n}\n\nexport function bindToState(obj, state) {\n\tvar newObj = {}\n\tObject.keys(obj)\n .filter(key => typeof obj[key] === \"function\")\n .forEach(key => newObj[key] = obj[key].bind(null, state))\n\treturn newObj\n}\n\nexport function normalizeArray(arr) {\n if(Array.isArray(arr))\n return arr\n return [arr]\n}\n\nexport function isFn(fn) {\n return typeof fn === \"function\"\n}\n\nexport function isObject(obj) {\n return !!obj && typeof obj === \"object\"\n}\n\nexport function isFunc(thing) {\n return typeof(thing) === \"function\"\n}\n\nexport function isArray(thing) {\n return Array.isArray(thing)\n}\n\n// I've changed memoize libs more than once, so I'm using this a way to make that simpler\nexport const memoize = _memoize\n\nexport function objMap(obj, fn) {\n return Object.keys(obj).reduce((newObj, key) => {\n newObj[key] = fn(obj[key], key)\n return newObj\n }, {})\n}\n\nexport function objReduce(obj, fn) {\n return Object.keys(obj).reduce((newObj, key) => {\n let res = fn(obj[key], key)\n if(res && typeof res === \"object\")\n Object.assign(newObj, res)\n return newObj\n }, {})\n}\n\n// Redux middleware that exposes the system to async actions (like redux-thunk, but with out system instead of (dispatch, getState)\nexport function systemThunkMiddleware(getSystem) {\n return ({ dispatch, getState }) => { // eslint-disable-line no-unused-vars\n return next => action => {\n if (typeof action === \"function\") {\n return action(getSystem())\n }\n\n return next(action)\n }\n }\n}\n\nexport function defaultStatusCode ( responses ) {\n let codes = responses.keySeq()\n return codes.contains(DEFAULT_RESPONSE_KEY) ? DEFAULT_RESPONSE_KEY : codes.filter( key => (key+\"\")[0] === \"2\").sort().first()\n}\n\n\n/**\n * Returns an Immutable List, safely\n * @param {Immutable.Iterable} iterable the iterable to get the key from\n * @param {String|[String]} key either an array of keys, or a single key\n * @returns {Immutable.List} either iterable.get(keys) or an empty Immutable.List\n */\nexport function getList(iterable, keys) {\n if(!Im.Iterable.isIterable(iterable)) {\n return Im.List()\n }\n let val = iterable.getIn(Array.isArray(keys) ? keys : [keys])\n return Im.List.isList(val) ? val : Im.List()\n}\n\n/**\n * Take an immutable map, and convert to a list.\n * Where the keys are merged with the value objects\n * @param {Immutable.Map} map, the map to convert\n * @param {String} key the key to use, when merging the `key`\n * @returns {Immutable.List}\n */\nexport function mapToList(map, keyNames=\"key\", collectedKeys=Im.Map()) {\n if(!Im.Map.isMap(map) || !map.size) {\n return Im.List()\n }\n\n if(!Array.isArray(keyNames)) {\n keyNames = [ keyNames ]\n }\n\n if(keyNames.length < 1) {\n return map.merge(collectedKeys)\n }\n\n // I need to avoid `flatMap` from merging in the Maps, as well as the lists\n let list = Im.List()\n let keyName = keyNames[0]\n for(let entry of map.entries()) {\n let [key, val] = entry\n let nextList = mapToList(val, keyNames.slice(1), collectedKeys.set(keyName, key))\n if(Im.List.isList(nextList)) {\n list = list.concat(nextList)\n } else {\n list = list.push(nextList)\n }\n }\n\n return list\n}\n\nexport function extractFileNameFromContentDispositionHeader(value){\n let patterns = [\n /filename\\*=[^']+'\\w*'\"([^\"]+)\";?/i,\n /filename\\*=[^']+'\\w*'([^;]+);?/i,\n /filename=\"([^;]*);?\"/i,\n /filename=([^;]*);?/i\n ]\n\n let responseFilename\n patterns.some(regex => {\n responseFilename = regex.exec(value)\n return responseFilename !== null\n })\n\n if (responseFilename !== null && responseFilename.length > 1) {\n try {\n return decodeURIComponent(responseFilename[1])\n } catch(e) {\n console.error(e)\n }\n }\n\n return null\n}\n\n// PascalCase, aka UpperCamelCase\nexport function pascalCase(str) {\n return upperFirst(camelCase(str))\n}\n\n// Remove the ext of a filename, and pascalCase it\nexport function pascalCaseFilename(filename) {\n return pascalCase(filename.replace(/\\.[^./]*$/, \"\"))\n}\n\n// Check if ...\n// - new props\n// - If immutable, use .is()\n// - if in explicit objectList, then compare using _.eq\n// - else use ===\nexport const propChecker = (props, nextProps, objectList=[], ignoreList=[]) => {\n\n if(Object.keys(props).length !== Object.keys(nextProps).length) {\n return true\n }\n\n return (\n some(props, (a, name) => {\n if(ignoreList.includes(name)) {\n return false\n }\n let b = nextProps[name]\n\n if(Im.Iterable.isIterable(a)) {\n return !Im.is(a,b)\n }\n\n // Not going to compare objects\n if(typeof a === \"object\" && typeof b === \"object\") {\n return false\n }\n\n return a !== b\n })\n || objectList.some( objectPropName => !eq(props[objectPropName], nextProps[objectPropName])))\n}\n\nexport const validateMaximum = ( val, max ) => {\n if (val > max) {\n return `Value must be less than ${max}`\n }\n}\n\nexport const validateMinimum = ( val, min ) => {\n if (val < min) {\n return `Value must be greater than ${min}`\n }\n}\n\nexport const validateNumber = ( val ) => {\n if (!/^-?\\d+(\\.?\\d+)?$/.test(val)) {\n return \"Value must be a number\"\n }\n}\n\nexport const validateInteger = ( val ) => {\n if (!/^-?\\d+$/.test(val)) {\n return \"Value must be an integer\"\n }\n}\n\nexport const validateFile = ( val ) => {\n if ( val && !(val instanceof win.File) ) {\n return \"Value must be a file\"\n }\n}\n\nexport const validateBoolean = ( val ) => {\n if ( !(val === \"true\" || val === \"false\" || val === true || val === false) ) {\n return \"Value must be a boolean\"\n }\n}\n\nexport const validateString = ( val ) => {\n if ( val && typeof val !== \"string\" ) {\n return \"Value must be a string\"\n }\n}\n\nexport const validateDateTime = (val) => {\n if (isNaN(Date.parse(val))) {\n return \"Value must be a DateTime\"\n }\n}\n\nexport const validateGuid = (val) => {\n val = val.toString().toLowerCase()\n if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(val)) {\n return \"Value must be a Guid\"\n }\n}\n\nexport const validateMaxLength = (val, max) => {\n if (val.length > max) {\n return `Value must be no longer than ${max} character${max !== 1 ? \"s\" : \"\"}`\n }\n}\n\nexport const validateUniqueItems = (val, uniqueItems) => {\n if (!val) {\n return\n }\n if (uniqueItems === \"true\" || uniqueItems === true) {\n const list = fromJS(val)\n const set = list.toSet()\n const hasDuplicates = val.length > set.size\n if(hasDuplicates) {\n let errorsPerIndex = Set()\n list.forEach((item, i) => {\n if(list.filter(v => isFunc(v.equals) ? v.equals(item) : v === item).size > 1) {\n errorsPerIndex = errorsPerIndex.add(i)\n }\n })\n if(errorsPerIndex.size !== 0) {\n return errorsPerIndex.map(i => ({index: i, error: \"No duplicates allowed.\"})).toArray()\n }\n }\n }\n}\n\nexport const validateMinItems = (val, min) => {\n if (!val && min >= 1 || val && val.length < min) {\n return `Array must contain at least ${min} item${min === 1 ? \"\" : \"s\"}`\n }\n}\n\nexport const validateMaxItems = (val, max) => {\n if (val && val.length > max) {\n return `Array must not contain more then ${max} item${max === 1 ? \"\" : \"s\"}`\n }\n}\n\nexport const validateMinLength = (val, min) => {\n if (val.length < min) {\n return `Value must be at least ${min} character${min !== 1 ? \"s\" : \"\"}`\n }\n}\n\nexport const validatePattern = (val, rxPattern) => {\n var patt = new RegExp(rxPattern)\n if (!patt.test(val)) {\n return \"Value must follow pattern \" + rxPattern\n }\n}\n\nfunction validateValueBySchema(value, schema, requiredByParam, bypassRequiredCheck, parameterContentMediaType) {\n if(!schema) return []\n let errors = []\n let nullable = schema.get(\"nullable\")\n let requiredBySchema = schema.get(\"required\")\n let maximum = schema.get(\"maximum\")\n let minimum = schema.get(\"minimum\")\n let type = schema.get(\"type\")\n let format = schema.get(\"format\")\n let maxLength = schema.get(\"maxLength\")\n let minLength = schema.get(\"minLength\")\n let uniqueItems = schema.get(\"uniqueItems\")\n let maxItems = schema.get(\"maxItems\")\n let minItems = schema.get(\"minItems\")\n let pattern = schema.get(\"pattern\")\n\n const schemaRequiresValue = requiredByParam || requiredBySchema === true\n const hasValue = value !== undefined && value !== null\n const isValidEmpty = !schemaRequiresValue && !hasValue\n\n const needsExplicitConstraintValidation = hasValue && type === \"array\"\n\n const requiresFurtherValidation =\n schemaRequiresValue\n || needsExplicitConstraintValidation\n || !isValidEmpty\n\n const isValidNullable = nullable && value === null\n\n // will not be included in the request or [schema / value] does not [allow / require] further analysis.\n const noFurtherValidationNeeded =\n isValidNullable\n || !type\n || !requiresFurtherValidation\n\n if(noFurtherValidationNeeded) {\n return []\n }\n\n // Further this point the parameter is considered worth to validate\n let stringCheck = type === \"string\" && value\n let arrayCheck = type === \"array\" && Array.isArray(value) && value.length\n let arrayListCheck = type === \"array\" && Im.List.isList(value) && value.count()\n let arrayStringCheck = type === \"array\" && typeof value === \"string\" && value\n let fileCheck = type === \"file\" && value instanceof win.File\n let booleanCheck = type === \"boolean\" && (value || value === false)\n let numberCheck = type === \"number\" && (value || value === 0)\n let integerCheck = type === \"integer\" && (value || value === 0)\n let objectCheck = type === \"object\" && typeof value === \"object\" && value !== null\n let objectStringCheck = type === \"object\" && typeof value === \"string\" && value\n\n const allChecks = [\n stringCheck, arrayCheck, arrayListCheck, arrayStringCheck, fileCheck,\n booleanCheck, numberCheck, integerCheck, objectCheck, objectStringCheck,\n ]\n\n const passedAnyCheck = allChecks.some(v => !!v)\n\n if (schemaRequiresValue && !passedAnyCheck && !bypassRequiredCheck) {\n errors.push(\"Required field is not provided\")\n return errors\n }\n if (\n type === \"object\" &&\n (parameterContentMediaType === null ||\n parameterContentMediaType === \"application/json\")\n ) {\n let objectVal = value\n if(typeof value === \"string\") {\n try {\n objectVal = JSON.parse(value)\n } catch (e) {\n errors.push(\"Parameter string value must be valid JSON\")\n return errors\n }\n }\n if(schema && schema.has(\"required\") && isFunc(requiredBySchema.isList) && requiredBySchema.isList()) {\n requiredBySchema.forEach(key => {\n if(objectVal[key] === undefined) {\n errors.push({ propKey: key, error: \"Required property not found\" })\n }\n })\n }\n if(schema && schema.has(\"properties\")) {\n schema.get(\"properties\").forEach((val, key) => {\n const errs = validateValueBySchema(objectVal[key], val, false, bypassRequiredCheck, parameterContentMediaType)\n errors.push(...errs\n .map((error) => ({ propKey: key, error })))\n })\n }\n }\n\n if (pattern) {\n let err = validatePattern(value, pattern)\n if (err) errors.push(err)\n }\n\n if (minItems) {\n if (type === \"array\") {\n let err = validateMinItems(value, minItems)\n if (err) errors.push(err)\n }\n }\n\n if (maxItems) {\n if (type === \"array\") {\n let err = validateMaxItems(value, maxItems)\n if (err) errors.push({ needRemove: true, error: err })\n }\n }\n\n if (uniqueItems) {\n if (type === \"array\") {\n let errorPerItem = validateUniqueItems(value, uniqueItems)\n if (errorPerItem) errors.push(...errorPerItem)\n }\n }\n\n if (maxLength || maxLength === 0) {\n let err = validateMaxLength(value, maxLength)\n if (err) errors.push(err)\n }\n\n if (minLength) {\n let err = validateMinLength(value, minLength)\n if (err) errors.push(err)\n }\n\n if (maximum || maximum === 0) {\n let err = validateMaximum(value, maximum)\n if (err) errors.push(err)\n }\n\n if (minimum || minimum === 0) {\n let err = validateMinimum(value, minimum)\n if (err) errors.push(err)\n }\n\n if (type === \"string\") {\n let err\n if (format === \"date-time\") {\n err = validateDateTime(value)\n } else if (format === \"uuid\") {\n err = validateGuid(value)\n } else {\n err = validateString(value)\n }\n if (!err) return errors\n errors.push(err)\n } else if (type === \"boolean\") {\n let err = validateBoolean(value)\n if (!err) return errors\n errors.push(err)\n } else if (type === \"number\") {\n let err = validateNumber(value)\n if (!err) return errors\n errors.push(err)\n } else if (type === \"integer\") {\n let err = validateInteger(value)\n if (!err) return errors\n errors.push(err)\n } else if (type === \"array\") {\n if (!(arrayCheck || arrayListCheck)) {\n return errors\n }\n if(value) {\n value.forEach((item, i) => {\n const errs = validateValueBySchema(item, schema.get(\"items\"), false, bypassRequiredCheck, parameterContentMediaType)\n errors.push(...errs\n .map((err) => ({ index: i, error: err })))\n })\n }\n } else if (type === \"file\") {\n let err = validateFile(value)\n if (!err) return errors\n errors.push(err)\n }\n\n return errors\n}\n\n// validation of parameters before execute\nexport const validateParam = (param, value, { isOAS3 = false, bypassRequiredCheck = false } = {}) => {\n\n let paramRequired = param.get(\"required\")\n\n let { schema: paramDetails, parameterContentMediaType } = getParameterSchema(param, { isOAS3 })\n\n return validateValueBySchema(value, paramDetails, paramRequired, bypassRequiredCheck, parameterContentMediaType)\n}\n\nconst getXmlSampleSchema = (schema, config, exampleOverride) => {\n if (schema && (!schema.xml || !schema.xml.name)) {\n schema.xml = schema.xml || {}\n\n if (schema.$$ref) {\n let match = schema.$$ref.match(/\\S*\\/(\\S+)$/)\n schema.xml.name = match[1]\n } else if (schema.type || schema.items || schema.properties || schema.additionalProperties) {\n return \"\\n\"\n } else {\n return null\n }\n }\n return memoizedCreateXMLExample(schema, config, exampleOverride)\n}\n\nconst shouldStringifyTypesConfig = [\n {\n when: /json/,\n shouldStringifyTypes: [\"string\"]\n }\n]\n\nconst defaultStringifyTypes = [\"object\"]\n\nconst getStringifiedSampleForSchema = (schema, config, contentType, exampleOverride) => {\n const res = memoizedSampleFromSchema(schema, config, exampleOverride)\n const resType = typeof res\n\n const typesToStringify = shouldStringifyTypesConfig.reduce(\n (types, nextConfig) => nextConfig.when.test(contentType)\n ? [...types, ...nextConfig.shouldStringifyTypes]\n : types,\n defaultStringifyTypes)\n\n return some(typesToStringify, x => x === resType)\n ? JSON.stringify(res, null, 2)\n : res\n}\n\nconst getYamlSampleSchema = (schema, config, contentType, exampleOverride) => {\n const jsonExample = getStringifiedSampleForSchema(schema, config, contentType, exampleOverride)\n let yamlString\n try {\n yamlString = YAML.dump(YAML.load(jsonExample), {\n\n lineWidth: -1 // don't generate line folds\n }, { schema: JSON_SCHEMA })\n if(yamlString[yamlString.length - 1] === \"\\n\") {\n yamlString = yamlString.slice(0, yamlString.length - 1)\n }\n } catch (e) {\n console.error(e)\n return \"error: could not generate yaml example\"\n }\n return yamlString\n .replace(/\\t/g, \" \")\n}\n\nexport const getSampleSchema = (schema, contentType=\"\", config={}, exampleOverride = undefined) => {\n if(schema && isFunc(schema.toJS))\n schema = schema.toJS()\n if(exampleOverride && isFunc(exampleOverride.toJS))\n exampleOverride = exampleOverride.toJS()\n\n if (/xml/.test(contentType)) {\n return getXmlSampleSchema(schema, config, exampleOverride)\n }\n if (/(yaml|yml)/.test(contentType)) {\n return getYamlSampleSchema(schema, config, contentType, exampleOverride)\n }\n return getStringifiedSampleForSchema(schema, config, contentType, exampleOverride)\n}\n\nexport const parseSearch = () => {\n let map = {}\n let search = win.location.search\n\n if(!search)\n return {}\n\n if ( search != \"\" ) {\n let params = search.substr(1).split(\"&\")\n\n for (let i in params) {\n if (!Object.prototype.hasOwnProperty.call(params, i)) {\n continue\n }\n i = params[i].split(\"=\")\n map[decodeURIComponent(i[0])] = (i[1] && decodeURIComponent(i[1])) || \"\"\n }\n }\n\n return map\n}\n\nexport const serializeSearch = (searchMap) => {\n return Object.keys(searchMap).map(k => {\n return encodeURIComponent(k) + \"=\" + encodeURIComponent(searchMap[k])\n }).join(\"&\")\n}\n\nexport const btoa = (str) => {\n let buffer\n\n if (str instanceof Buffer) {\n buffer = str\n } else {\n buffer = Buffer.from(str.toString(), \"utf-8\")\n }\n\n return buffer.toString(\"base64\")\n}\n\nexport const sorters = {\n operationsSorter: {\n alpha: (a, b) => a.get(\"path\").localeCompare(b.get(\"path\")),\n method: (a, b) => a.get(\"method\").localeCompare(b.get(\"method\"))\n },\n tagsSorter: {\n alpha: (a, b) => a.localeCompare(b)\n }\n}\n\nexport const buildFormData = (data) => {\n let formArr = []\n\n for (let name in data) {\n let val = data[name]\n if (val !== undefined && val !== \"\") {\n formArr.push([name, \"=\", encodeURIComponent(val).replace(/%20/g,\"+\")].join(\"\"))\n }\n }\n return formArr.join(\"&\")\n}\n\n// Is this really required as a helper? Perhaps. TODO: expose the system of presets.apis in docs, so we know what is supported\nexport const shallowEqualKeys = (a,b, keys) => {\n return !!find(keys, (key) => {\n return eq(a[key], b[key])\n })\n}\n\nexport function sanitizeUrl(url) {\n if(typeof url !== \"string\" || url === \"\") {\n return \"\"\n }\n\n return braintreeSanitizeUrl(url)\n}\n\nexport function requiresValidationURL(uri) {\n if (!uri || uri.indexOf(\"localhost\") >= 0 || uri.indexOf(\"127.0.0.1\") >= 0 || uri === \"none\") {\n return false\n }\n return true\n}\n\n\nexport function getAcceptControllingResponse(responses) {\n if(!Im.OrderedMap.isOrderedMap(responses)) {\n // wrong type!\n return null\n }\n\n if(!responses.size) {\n // responses is empty\n return null\n }\n\n const suitable2xxResponse = responses.find((res, k) => {\n return k.startsWith(\"2\") && Object.keys(res.get(\"content\") || {}).length > 0\n })\n\n // try to find a suitable `default` responses\n const defaultResponse = responses.get(\"default\") || Im.OrderedMap()\n const defaultResponseMediaTypes = (defaultResponse.get(\"content\") || Im.OrderedMap()).keySeq().toJS()\n const suitableDefaultResponse = defaultResponseMediaTypes.length ? defaultResponse : null\n\n return suitable2xxResponse || suitableDefaultResponse\n}\n\n// suitable for use in URL fragments\nexport const createDeepLinkPath = (str) => typeof str == \"string\" || str instanceof String ? str.trim().replace(/\\s/g, \"%20\") : \"\"\n// suitable for use in CSS classes and ids\nexport const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str).replace(/%20/g, \"_\") )\n\nexport const getExtensions = (defObj) => defObj.filter((v, k) => /^x-/.test(k))\nexport const getCommonExtensions = (defObj) => defObj.filter((v, k) => /^pattern|maxLength|minLength|maximum|minimum/.test(k))\n\n// Deeply strips a specific key from an object.\n//\n// `predicate` can be used to discriminate the stripping further,\n// by preserving the key's place in the object based on its value.\nexport function deeplyStripKey(input, keyToStrip, predicate = () => true) {\n if(typeof input !== \"object\" || Array.isArray(input) || input === null || !keyToStrip) {\n return input\n }\n\n const obj = Object.assign({}, input)\n\n Object.keys(obj).forEach(k => {\n if(k === keyToStrip && predicate(obj[k], k)) {\n delete obj[k]\n return\n }\n obj[k] = deeplyStripKey(obj[k], keyToStrip, predicate)\n })\n\n return obj\n}\n\nexport function stringify(thing) {\n if (typeof thing === \"string\") {\n return thing\n }\n\n if (thing && thing.toJS) {\n thing = thing.toJS()\n }\n\n if (typeof thing === \"object\" && thing !== null) {\n try {\n return JSON.stringify(thing, null, 2)\n }\n catch (e) {\n return String(thing)\n }\n }\n\n if(thing === null || thing === undefined) {\n return \"\"\n }\n\n return thing.toString()\n}\n\nexport function numberToString(thing) {\n if(typeof thing === \"number\") {\n return thing.toString()\n }\n\n return thing\n}\n\nexport function paramToIdentifier(param, { returnAll = false, allowHashes = true } = {}) {\n if(!Im.Map.isMap(param)) {\n throw new Error(\"paramToIdentifier: received a non-Im.Map parameter as input\")\n }\n const paramName = param.get(\"name\")\n const paramIn = param.get(\"in\")\n\n let generatedIdentifiers = []\n\n // Generate identifiers in order of most to least specificity\n\n if (param && param.hashCode && paramIn && paramName && allowHashes) {\n generatedIdentifiers.push(`${paramIn}.${paramName}.hash-${param.hashCode()}`)\n }\n\n if(paramIn && paramName) {\n generatedIdentifiers.push(`${paramIn}.${paramName}`)\n }\n\n generatedIdentifiers.push(paramName)\n\n // Return the most preferred identifier, or all if requested\n\n return returnAll ? generatedIdentifiers : (generatedIdentifiers[0] || \"\")\n}\n\nexport function paramToValue(param, paramValues) {\n const allIdentifiers = paramToIdentifier(param, { returnAll: true })\n\n // Map identifiers to values in the provided value hash, filter undefined values,\n // and return the first value found\n const values = allIdentifiers\n .map(id => {\n return paramValues[id]\n })\n .filter(value => value !== undefined)\n\n return values[0]\n}\n\n// adapted from https://auth0.com/docs/flows/guides/auth-code-pkce/includes/create-code-verifier\nexport function generateCodeVerifier() {\n return b64toB64UrlEncoded(\n randomBytes(32).toString(\"base64\")\n )\n}\n\nexport function createCodeChallenge(codeVerifier) {\n return b64toB64UrlEncoded(\n shaJs(\"sha256\")\n .update(codeVerifier)\n .digest(\"base64\")\n )\n}\n\nfunction b64toB64UrlEncoded(str) {\n return str\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=/g, \"\")\n}\n\nexport const isEmptyValue = (value) => {\n if (!value) {\n return true\n }\n\n if (isImmutable(value) && value.isEmpty()) {\n return true\n }\n\n return false\n}\n","export function canJsonParse(str) {\n try {\n let testValueForJson = JSON.parse(str)\n return testValueForJson ? true : false\n } catch (e) {\n // exception: string is not valid json\n return null\n }\n}\n\nexport function getKnownSyntaxHighlighterLanguage(val) {\n // to start, only check for json. can expand as needed in future\n const isValidJson = canJsonParse(val)\n return isValidJson ? \"json\" : null\n}\n","function makeWindow() {\n var win = {\n location: {},\n history: {},\n open: () => {},\n close: () => {},\n File: function() {}\n }\n\n if(typeof window === \"undefined\") {\n return win\n }\n\n try {\n win = window\n var props = [\"File\", \"Blob\", \"FormData\"]\n for (var prop of props) {\n if (prop in window) {\n win[prop] = window[prop]\n }\n }\n } catch( e ) {\n console.error(e)\n }\n\n return win\n}\n\nexport default makeWindow()\n","/**\n * @prettier\n */\n\nimport Im from \"immutable\"\n\nconst swagger2SchemaKeys = Im.Set.of(\n \"type\",\n \"format\",\n \"items\",\n \"default\",\n \"maximum\",\n \"exclusiveMaximum\",\n \"minimum\",\n \"exclusiveMinimum\",\n \"maxLength\",\n \"minLength\",\n \"pattern\",\n \"maxItems\",\n \"minItems\",\n \"uniqueItems\",\n \"enum\",\n \"multipleOf\"\n)\n\n/**\n * @typedef {Object} ParameterSchemaDescriptor\n * @property {Immutable.Map} schema - the parameter schema\n * @property {string|null} parameterContentMediaType - the effective media type, for `content`-based OpenAPI 3.0 Parameters, or `null` otherwise\n */\n\n/**\n * Get the effective schema value for a parameter, or an empty Immutable.Map if\n * no suitable schema can be found.\n *\n * Supports OpenAPI 3.0 `Parameter.content` priority -- since a Parameter Object\n * cannot have both `schema` and `content`, this function ignores `schema` when\n * `content` is present.\n *\n * @param {Immutable.Map} parameter The parameter to identify a schema for\n * @param {object} config\n * @param {boolean} config.isOAS3 Whether the parameter is from an OpenAPI 2.0\n * or OpenAPI 3.0 definition\n * @return {ParameterSchemaDescriptor} Information about the parameter schema\n */\nexport default function getParameterSchema(parameter, { isOAS3 } = {}) {\n // Return empty Map if `parameter` isn't a Map\n if (!Im.Map.isMap(parameter)) {\n return {\n schema: Im.Map(),\n parameterContentMediaType: null,\n }\n }\n\n if (!isOAS3) {\n // Swagger 2.0\n if (parameter.get(\"in\") === \"body\") {\n return {\n schema: parameter.get(\"schema\", Im.Map()),\n parameterContentMediaType: null,\n }\n } else {\n return {\n schema: parameter.filter((v, k) => swagger2SchemaKeys.includes(k)),\n parameterContentMediaType: null,\n }\n }\n }\n\n // If we've reached here, the parameter is OpenAPI 3.0\n\n if (parameter.get(\"content\")) {\n const parameterContentMediaTypes = parameter\n .get(\"content\", Im.Map({}))\n .keySeq()\n\n const parameterContentMediaType = parameterContentMediaTypes.first()\n\n return {\n schema: parameter.getIn(\n [\"content\", parameterContentMediaType, \"schema\"],\n Im.Map()\n ),\n parameterContentMediaType,\n }\n }\n\n return {\n schema: parameter.get(\"schema\", Im.Map()),\n parameterContentMediaType: null,\n }\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_get_80914616__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_getPrototypeOf_c21fe081__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_wrapNativeSuper_31d78c7a__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_find_index_99e05360__[\"default\"] });","import memoize from \"lodash/memoize\"\n\n/**\n * This function is extension on top of lodash.memoize.\n * It uses all the arguments of the `fn` as the cache key instead of just the first one.\n * If resolver is provided, it determines the cache key for\n * storing the result based on the arguments provided to the memoized function.\n */\n\nconst shallowArrayEquals = (a) => (b) => {\n return Array.isArray(a) && Array.isArray(b)\n && a.length === b.length\n && a.every((val, index) => val === b[index])\n}\n\nconst list = (...args) => args\n\nclass Cache extends Map {\n delete(key) {\n const keys = Array.from(this.keys())\n const foundKey = keys.find(shallowArrayEquals(key))\n return super.delete(foundKey)\n }\n\n get(key) {\n const keys = Array.from(this.keys())\n const foundKey = keys.find(shallowArrayEquals(key))\n return super.get(foundKey)\n }\n\n has(key) {\n const keys = Array.from(this.keys())\n return keys.findIndex(shallowArrayEquals(key)) !== -1\n }\n}\n\nconst memoizeN = (fn, resolver = list) => {\n const { Cache: OriginalCache } = memoize\n memoize.Cache = Cache\n\n const memoized = memoize(fn, resolver)\n\n memoize.Cache = OriginalCache\n\n return memoized\n}\n\nexport default memoizeN\n","/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n/* eslint-disable no-proto */\n\n'use strict'\n\nconst base64 = require('base64-js')\nconst ieee754 = require('ieee754')\nconst customInspectSymbol =\n (typeof Symbol === 'function' && typeof Symbol['for'] === 'function') // eslint-disable-line dot-notation\n ? Symbol['for']('nodejs.util.inspect.custom') // eslint-disable-line dot-notation\n : null\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\nconst K_MAX_LENGTH = 0x7fffffff\nexports.kMaxLength = K_MAX_LENGTH\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Print warning and recommend using `buffer` v4.x which has an Object\n * implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * We report that the browser does not support typed arrays if the are not subclassable\n * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`\n * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support\n * for __proto__ and has a buggy typed array implementation.\n */\nBuffer.TYPED_ARRAY_SUPPORT = typedArraySupport()\n\nif (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&\n typeof console.error === 'function') {\n console.error(\n 'This browser lacks typed array (Uint8Array) support which is required by ' +\n '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'\n )\n}\n\nfunction typedArraySupport () {\n // Can typed array instances can be augmented?\n try {\n const arr = new Uint8Array(1)\n const proto = { foo: function () { return 42 } }\n Object.setPrototypeOf(proto, Uint8Array.prototype)\n Object.setPrototypeOf(arr, proto)\n return arr.foo() === 42\n } catch (e) {\n return false\n }\n}\n\nObject.defineProperty(Buffer.prototype, 'parent', {\n enumerable: true,\n get: function () {\n if (!Buffer.isBuffer(this)) return undefined\n return this.buffer\n }\n})\n\nObject.defineProperty(Buffer.prototype, 'offset', {\n enumerable: true,\n get: function () {\n if (!Buffer.isBuffer(this)) return undefined\n return this.byteOffset\n }\n})\n\nfunction createBuffer (length) {\n if (length > K_MAX_LENGTH) {\n throw new RangeError('The value \"' + length + '\" is invalid for option \"size\"')\n }\n // Return an augmented `Uint8Array` instance\n const buf = new Uint8Array(length)\n Object.setPrototypeOf(buf, Buffer.prototype)\n return buf\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new TypeError(\n 'The \"string\" argument must be of type string. Received type number'\n )\n }\n return allocUnsafe(arg)\n }\n return from(arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\nfunction from (value, encodingOrOffset, length) {\n if (typeof value === 'string') {\n return fromString(value, encodingOrOffset)\n }\n\n if (ArrayBuffer.isView(value)) {\n return fromArrayView(value)\n }\n\n if (value == null) {\n throw new TypeError(\n 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +\n 'or Array-like Object. Received type ' + (typeof value)\n )\n }\n\n if (isInstance(value, ArrayBuffer) ||\n (value && isInstance(value.buffer, ArrayBuffer))) {\n return fromArrayBuffer(value, encodingOrOffset, length)\n }\n\n if (typeof SharedArrayBuffer !== 'undefined' &&\n (isInstance(value, SharedArrayBuffer) ||\n (value && isInstance(value.buffer, SharedArrayBuffer)))) {\n return fromArrayBuffer(value, encodingOrOffset, length)\n }\n\n if (typeof value === 'number') {\n throw new TypeError(\n 'The \"value\" argument must not be of type number. Received type number'\n )\n }\n\n const valueOf = value.valueOf && value.valueOf()\n if (valueOf != null && valueOf !== value) {\n return Buffer.from(valueOf, encodingOrOffset, length)\n }\n\n const b = fromObject(value)\n if (b) return b\n\n if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&\n typeof value[Symbol.toPrimitive] === 'function') {\n return Buffer.from(value[Symbol.toPrimitive]('string'), encodingOrOffset, length)\n }\n\n throw new TypeError(\n 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +\n 'or Array-like Object. Received type ' + (typeof value)\n )\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(value, encodingOrOffset, length)\n}\n\n// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:\n// https://github.com/feross/buffer/pull/148\nObject.setPrototypeOf(Buffer.prototype, Uint8Array.prototype)\nObject.setPrototypeOf(Buffer, Uint8Array)\n\nfunction assertSize (size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be of type number')\n } else if (size < 0) {\n throw new RangeError('The value \"' + size + '\" is invalid for option \"size\"')\n }\n}\n\nfunction alloc (size, fill, encoding) {\n assertSize(size)\n if (size <= 0) {\n return createBuffer(size)\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpreted as a start offset.\n return typeof encoding === 'string'\n ? createBuffer(size).fill(fill, encoding)\n : createBuffer(size).fill(fill)\n }\n return createBuffer(size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(size, fill, encoding)\n}\n\nfunction allocUnsafe (size) {\n assertSize(size)\n return createBuffer(size < 0 ? 0 : checked(size) | 0)\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(size)\n}\n\nfunction fromString (string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8'\n }\n\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n\n const length = byteLength(string, encoding) | 0\n let buf = createBuffer(length)\n\n const actual = buf.write(string, encoding)\n\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n buf = buf.slice(0, actual)\n }\n\n return buf\n}\n\nfunction fromArrayLike (array) {\n const length = array.length < 0 ? 0 : checked(array.length) | 0\n const buf = createBuffer(length)\n for (let i = 0; i < length; i += 1) {\n buf[i] = array[i] & 255\n }\n return buf\n}\n\nfunction fromArrayView (arrayView) {\n if (isInstance(arrayView, Uint8Array)) {\n const copy = new Uint8Array(arrayView)\n return fromArrayBuffer(copy.buffer, copy.byteOffset, copy.byteLength)\n }\n return fromArrayLike(arrayView)\n}\n\nfunction fromArrayBuffer (array, byteOffset, length) {\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\"offset\" is outside of buffer bounds')\n }\n\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\"length\" is outside of buffer bounds')\n }\n\n let buf\n if (byteOffset === undefined && length === undefined) {\n buf = new Uint8Array(array)\n } else if (length === undefined) {\n buf = new Uint8Array(array, byteOffset)\n } else {\n buf = new Uint8Array(array, byteOffset, length)\n }\n\n // Return an augmented `Uint8Array` instance\n Object.setPrototypeOf(buf, Buffer.prototype)\n\n return buf\n}\n\nfunction fromObject (obj) {\n if (Buffer.isBuffer(obj)) {\n const len = checked(obj.length) | 0\n const buf = createBuffer(len)\n\n if (buf.length === 0) {\n return buf\n }\n\n obj.copy(buf, 0, 0, len)\n return buf\n }\n\n if (obj.length !== undefined) {\n if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {\n return createBuffer(0)\n }\n return fromArrayLike(obj)\n }\n\n if (obj.type === 'Buffer' && Array.isArray(obj.data)) {\n return fromArrayLike(obj.data)\n }\n}\n\nfunction checked (length) {\n // Note: cannot use `length < K_MAX_LENGTH` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= K_MAX_LENGTH) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')\n }\n return length | 0\n}\n\nfunction SlowBuffer (length) {\n if (+length != length) { // eslint-disable-line eqeqeq\n length = 0\n }\n return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n return b != null && b._isBuffer === true &&\n b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false\n}\n\nBuffer.compare = function compare (a, b) {\n if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)\n if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError(\n 'The \"buf1\", \"buf2\" arguments must be one of type Buffer or Uint8Array'\n )\n }\n\n if (a === b) return 0\n\n let x = a.length\n let y = b.length\n\n for (let i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i]\n y = b[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true\n default:\n return false\n }\n}\n\nBuffer.concat = function concat (list, length) {\n if (!Array.isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n\n if (list.length === 0) {\n return Buffer.alloc(0)\n }\n\n let i\n if (length === undefined) {\n length = 0\n for (i = 0; i < list.length; ++i) {\n length += list[i].length\n }\n }\n\n const buffer = Buffer.allocUnsafe(length)\n let pos = 0\n for (i = 0; i < list.length; ++i) {\n let buf = list[i]\n if (isInstance(buf, Uint8Array)) {\n if (pos + buf.length > buffer.length) {\n if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf)\n buf.copy(buffer, pos)\n } else {\n Uint8Array.prototype.set.call(\n buffer,\n buf,\n pos\n )\n }\n } else if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n } else {\n buf.copy(buffer, pos)\n }\n pos += buf.length\n }\n return buffer\n}\n\nfunction byteLength (string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length\n }\n if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {\n return string.byteLength\n }\n if (typeof string !== 'string') {\n throw new TypeError(\n 'The \"string\" argument must be one of type string, Buffer, or ArrayBuffer. ' +\n 'Received type ' + typeof string\n )\n }\n\n const len = string.length\n const mustMatch = (arguments.length > 2 && arguments[2] === true)\n if (!mustMatch && len === 0) return 0\n\n // Use a for loop to avoid recursion\n let loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len\n case 'utf8':\n case 'utf-8':\n return utf8ToBytes(string).length\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2\n case 'hex':\n return len >>> 1\n case 'base64':\n return base64ToBytes(string).length\n default:\n if (loweredCase) {\n return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8\n }\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n let loweredCase = false\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return ''\n }\n\n if (end === undefined || end > this.length) {\n end = this.length\n }\n\n if (end <= 0) {\n return ''\n }\n\n // Force coercion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0\n start >>>= 0\n\n if (end <= start) {\n return ''\n }\n\n if (!encoding) encoding = 'utf8'\n\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end)\n\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end)\n\n case 'ascii':\n return asciiSlice(this, start, end)\n\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end)\n\n case 'base64':\n return base64Slice(this, start, end)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = (encoding + '').toLowerCase()\n loweredCase = true\n }\n }\n}\n\n// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)\n// to detect a Buffer instance. It's not possible to use `instanceof Buffer`\n// reliably in a browserify context because there could be multiple different\n// copies of the 'buffer' package in use. This method works even for Buffer\n// instances that were created from another copy of the `buffer` package.\n// See: https://github.com/feross/buffer/issues/154\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n const i = b[n]\n b[n] = b[m]\n b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n const len = this.length\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits')\n }\n for (let i = 0; i < len; i += 2) {\n swap(this, i, i + 1)\n }\n return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n const len = this.length\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits')\n }\n for (let i = 0; i < len; i += 4) {\n swap(this, i, i + 3)\n swap(this, i + 1, i + 2)\n }\n return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n const len = this.length\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits')\n }\n for (let i = 0; i < len; i += 8) {\n swap(this, i, i + 7)\n swap(this, i + 1, i + 6)\n swap(this, i + 2, i + 5)\n swap(this, i + 3, i + 4)\n }\n return this\n}\n\nBuffer.prototype.toString = function toString () {\n const length = this.length\n if (length === 0) return ''\n if (arguments.length === 0) return utf8Slice(this, 0, length)\n return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.toLocaleString = Buffer.prototype.toString\n\nBuffer.prototype.equals = function equals (b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n if (this === b) return true\n return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n let str = ''\n const max = exports.INSPECT_MAX_BYTES\n str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()\n if (this.length > max) str += ' ... '\n return ''\n}\nif (customInspectSymbol) {\n Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n if (isInstance(target, Uint8Array)) {\n target = Buffer.from(target, target.offset, target.byteLength)\n }\n if (!Buffer.isBuffer(target)) {\n throw new TypeError(\n 'The \"target\" argument must be one of type Buffer or Uint8Array. ' +\n 'Received type ' + (typeof target)\n )\n }\n\n if (start === undefined) {\n start = 0\n }\n if (end === undefined) {\n end = target ? target.length : 0\n }\n if (thisStart === undefined) {\n thisStart = 0\n }\n if (thisEnd === undefined) {\n thisEnd = this.length\n }\n\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index')\n }\n\n if (thisStart >= thisEnd && start >= end) {\n return 0\n }\n if (thisStart >= thisEnd) {\n return -1\n }\n if (start >= end) {\n return 1\n }\n\n start >>>= 0\n end >>>= 0\n thisStart >>>= 0\n thisEnd >>>= 0\n\n if (this === target) return 0\n\n let x = thisEnd - thisStart\n let y = end - start\n const len = Math.min(x, y)\n\n const thisCopy = this.slice(thisStart, thisEnd)\n const targetCopy = target.slice(start, end)\n\n for (let i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i]\n y = targetCopy[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset\n byteOffset = 0\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000\n }\n byteOffset = +byteOffset // Coerce to Number.\n if (numberIsNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : (buffer.length - 1)\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n if (byteOffset >= buffer.length) {\n if (dir) return -1\n else byteOffset = buffer.length - 1\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0\n else return -1\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding)\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n } else if (typeof val === 'number') {\n val = val & 0xFF // Search for a byte value [0-255]\n if (typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n }\n }\n return arrayIndexOf(buffer, [val], byteOffset, encoding, dir)\n }\n\n throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n let indexSize = 1\n let arrLength = arr.length\n let valLength = val.length\n\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase()\n if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1\n }\n indexSize = 2\n arrLength /= 2\n valLength /= 2\n byteOffset /= 2\n }\n }\n\n function read (buf, i) {\n if (indexSize === 1) {\n return buf[i]\n } else {\n return buf.readUInt16BE(i * indexSize)\n }\n }\n\n let i\n if (dir) {\n let foundIndex = -1\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n } else {\n if (foundIndex !== -1) i -= i - foundIndex\n foundIndex = -1\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n for (i = byteOffset; i >= 0; i--) {\n let found = true\n for (let j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false\n break\n }\n }\n if (found) return i\n }\n }\n\n return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n offset = Number(offset) || 0\n const remaining = buf.length - offset\n if (!length) {\n length = remaining\n } else {\n length = Number(length)\n if (length > remaining) {\n length = remaining\n }\n }\n\n const strLen = string.length\n\n if (length > strLen / 2) {\n length = strLen / 2\n }\n let i\n for (i = 0; i < length; ++i) {\n const parsed = parseInt(string.substr(i * 2, 2), 16)\n if (numberIsNaN(parsed)) return i\n buf[offset + i] = parsed\n }\n return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8'\n length = this.length\n offset = 0\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset\n length = this.length\n offset = 0\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset >>> 0\n if (isFinite(length)) {\n length = length >>> 0\n if (encoding === undefined) encoding = 'utf8'\n } else {\n encoding = length\n length = undefined\n }\n } else {\n throw new Error(\n 'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n )\n }\n\n const remaining = this.length - offset\n if (length === undefined || length > remaining) length = remaining\n\n if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds')\n }\n\n if (!encoding) encoding = 'utf8'\n\n let loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length)\n\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length)\n\n case 'ascii':\n case 'latin1':\n case 'binary':\n return asciiWrite(this, string, offset, length)\n\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n }\n}\n\nfunction base64Slice (buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf)\n } else {\n return base64.fromByteArray(buf.slice(start, end))\n }\n}\n\nfunction utf8Slice (buf, start, end) {\n end = Math.min(buf.length, end)\n const res = []\n\n let i = start\n while (i < end) {\n const firstByte = buf[i]\n let codePoint = null\n let bytesPerSequence = (firstByte > 0xEF)\n ? 4\n : (firstByte > 0xDF)\n ? 3\n : (firstByte > 0xBF)\n ? 2\n : 1\n\n if (i + bytesPerSequence <= end) {\n let secondByte, thirdByte, fourthByte, tempCodePoint\n\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte\n }\n break\n case 2:\n secondByte = buf[i + 1]\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint\n }\n }\n break\n case 3:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint\n }\n }\n break\n case 4:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n fourthByte = buf[i + 3]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint\n }\n }\n }\n }\n\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD\n bytesPerSequence = 1\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000\n res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n codePoint = 0xDC00 | codePoint & 0x3FF\n }\n\n res.push(codePoint)\n i += bytesPerSequence\n }\n\n return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nconst MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n const len = codePoints.length\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n let res = ''\n let i = 0\n while (i < len) {\n res += String.fromCharCode.apply(\n String,\n codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n )\n }\n return res\n}\n\nfunction asciiSlice (buf, start, end) {\n let ret = ''\n end = Math.min(buf.length, end)\n\n for (let i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F)\n }\n return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n let ret = ''\n end = Math.min(buf.length, end)\n\n for (let i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i])\n }\n return ret\n}\n\nfunction hexSlice (buf, start, end) {\n const len = buf.length\n\n if (!start || start < 0) start = 0\n if (!end || end < 0 || end > len) end = len\n\n let out = ''\n for (let i = start; i < end; ++i) {\n out += hexSliceLookupTable[buf[i]]\n }\n return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n const bytes = buf.slice(start, end)\n let res = ''\n // If bytes.length is odd, the last 8 bits must be ignored (same as node.js)\n for (let i = 0; i < bytes.length - 1; i += 2) {\n res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))\n }\n return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n const len = this.length\n start = ~~start\n end = end === undefined ? len : ~~end\n\n if (start < 0) {\n start += len\n if (start < 0) start = 0\n } else if (start > len) {\n start = len\n }\n\n if (end < 0) {\n end += len\n if (end < 0) end = 0\n } else if (end > len) {\n end = len\n }\n\n if (end < start) end = start\n\n const newBuf = this.subarray(start, end)\n // Return an augmented `Uint8Array` instance\n Object.setPrototypeOf(newBuf, Buffer.prototype)\n\n return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUintLE =\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n let val = this[offset]\n let mul = 1\n let i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUintBE =\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length)\n }\n\n let val = this[offset + --byteLength]\n let mul = 1\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUint8 =\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 1, this.length)\n return this[offset]\n}\n\nBuffer.prototype.readUint16LE =\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUint16BE =\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUint32LE =\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return ((this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16)) +\n (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUint32BE =\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] * 0x1000000) +\n ((this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n this[offset + 3])\n}\n\nBuffer.prototype.readBigUInt64LE = defineBigIntMethod(function readBigUInt64LE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const lo = first +\n this[++offset] * 2 ** 8 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 24\n\n const hi = this[++offset] +\n this[++offset] * 2 ** 8 +\n this[++offset] * 2 ** 16 +\n last * 2 ** 24\n\n return BigInt(lo) + (BigInt(hi) << BigInt(32))\n})\n\nBuffer.prototype.readBigUInt64BE = defineBigIntMethod(function readBigUInt64BE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const hi = first * 2 ** 24 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n this[++offset]\n\n const lo = this[++offset] * 2 ** 24 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n last\n\n return (BigInt(hi) << BigInt(32)) + BigInt(lo)\n})\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n let val = this[offset]\n let mul = 1\n let i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n let i = byteLength\n let mul = 1\n let val = this[offset + --i]\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 1, this.length)\n if (!(this[offset] & 0x80)) return (this[offset])\n return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n const val = this[offset] | (this[offset + 1] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 2, this.length)\n const val = this[offset + 1] | (this[offset] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16) |\n (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] << 24) |\n (this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n (this[offset + 3])\n}\n\nBuffer.prototype.readBigInt64LE = defineBigIntMethod(function readBigInt64LE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const val = this[offset + 4] +\n this[offset + 5] * 2 ** 8 +\n this[offset + 6] * 2 ** 16 +\n (last << 24) // Overflow\n\n return (BigInt(val) << BigInt(32)) +\n BigInt(first +\n this[++offset] * 2 ** 8 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 24)\n})\n\nBuffer.prototype.readBigInt64BE = defineBigIntMethod(function readBigInt64BE (offset) {\n offset = offset >>> 0\n validateNumber(offset, 'offset')\n const first = this[offset]\n const last = this[offset + 7]\n if (first === undefined || last === undefined) {\n boundsError(offset, this.length - 8)\n }\n\n const val = (first << 24) + // Overflow\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n this[++offset]\n\n return (BigInt(val) << BigInt(32)) +\n BigInt(this[++offset] * 2 ** 24 +\n this[++offset] * 2 ** 16 +\n this[++offset] * 2 ** 8 +\n last)\n})\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n offset = offset >>> 0\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUintLE =\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) {\n const maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n let mul = 1\n let i = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUintBE =\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n byteLength = byteLength >>> 0\n if (!noAssert) {\n const maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n let i = byteLength - 1\n let mul = 1\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUint8 =\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeUint16LE =\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n return offset + 2\n}\n\nBuffer.prototype.writeUint16BE =\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n return offset + 2\n}\n\nBuffer.prototype.writeUint32LE =\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n this[offset + 3] = (value >>> 24)\n this[offset + 2] = (value >>> 16)\n this[offset + 1] = (value >>> 8)\n this[offset] = (value & 0xff)\n return offset + 4\n}\n\nBuffer.prototype.writeUint32BE =\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n return offset + 4\n}\n\nfunction wrtBigUInt64LE (buf, value, offset, min, max) {\n checkIntBI(value, min, max, buf, offset, 7)\n\n let lo = Number(value & BigInt(0xffffffff))\n buf[offset++] = lo\n lo = lo >> 8\n buf[offset++] = lo\n lo = lo >> 8\n buf[offset++] = lo\n lo = lo >> 8\n buf[offset++] = lo\n let hi = Number(value >> BigInt(32) & BigInt(0xffffffff))\n buf[offset++] = hi\n hi = hi >> 8\n buf[offset++] = hi\n hi = hi >> 8\n buf[offset++] = hi\n hi = hi >> 8\n buf[offset++] = hi\n return offset\n}\n\nfunction wrtBigUInt64BE (buf, value, offset, min, max) {\n checkIntBI(value, min, max, buf, offset, 7)\n\n let lo = Number(value & BigInt(0xffffffff))\n buf[offset + 7] = lo\n lo = lo >> 8\n buf[offset + 6] = lo\n lo = lo >> 8\n buf[offset + 5] = lo\n lo = lo >> 8\n buf[offset + 4] = lo\n let hi = Number(value >> BigInt(32) & BigInt(0xffffffff))\n buf[offset + 3] = hi\n hi = hi >> 8\n buf[offset + 2] = hi\n hi = hi >> 8\n buf[offset + 1] = hi\n hi = hi >> 8\n buf[offset] = hi\n return offset + 8\n}\n\nBuffer.prototype.writeBigUInt64LE = defineBigIntMethod(function writeBigUInt64LE (value, offset = 0) {\n return wrtBigUInt64LE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff'))\n})\n\nBuffer.prototype.writeBigUInt64BE = defineBigIntMethod(function writeBigUInt64BE (value, offset = 0) {\n return wrtBigUInt64BE(this, value, offset, BigInt(0), BigInt('0xffffffffffffffff'))\n})\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n const limit = Math.pow(2, (8 * byteLength) - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n let i = 0\n let mul = 1\n let sub = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n const limit = Math.pow(2, (8 * byteLength) - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n let i = byteLength - 1\n let mul = 1\n let sub = 0\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n if (value < 0) value = 0xff + value + 1\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n this[offset + 2] = (value >>> 16)\n this[offset + 3] = (value >>> 24)\n return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (value < 0) value = 0xffffffff + value + 1\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n return offset + 4\n}\n\nBuffer.prototype.writeBigInt64LE = defineBigIntMethod(function writeBigInt64LE (value, offset = 0) {\n return wrtBigUInt64LE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff'))\n})\n\nBuffer.prototype.writeBigInt64BE = defineBigIntMethod(function writeBigInt64BE (value, offset = 0) {\n return wrtBigUInt64BE(this, value, offset, -BigInt('0x8000000000000000'), BigInt('0x7fffffffffffffff'))\n})\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4)\n return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n value = +value\n offset = offset >>> 0\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8)\n return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')\n if (!start) start = 0\n if (!end && end !== 0) end = this.length\n if (targetStart >= target.length) targetStart = target.length\n if (!targetStart) targetStart = 0\n if (end > 0 && end < start) end = start\n\n // Copy 0 bytes; we're done\n if (end === start) return 0\n if (target.length === 0 || this.length === 0) return 0\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds')\n }\n if (start < 0 || start >= this.length) throw new RangeError('Index out of range')\n if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n // Are we oob?\n if (end > this.length) end = this.length\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start\n }\n\n const len = end - start\n\n if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {\n // Use built-in when available, missing from IE11\n this.copyWithin(targetStart, start, end)\n } else {\n Uint8Array.prototype.set.call(\n target,\n this.subarray(start, end),\n targetStart\n )\n }\n\n return len\n}\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start\n start = 0\n end = this.length\n } else if (typeof end === 'string') {\n encoding = end\n end = this.length\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string')\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n if (val.length === 1) {\n const code = val.charCodeAt(0)\n if ((encoding === 'utf8' && code < 128) ||\n encoding === 'latin1') {\n // Fast path: If `val` fits into a single byte, use that numeric value.\n val = code\n }\n }\n } else if (typeof val === 'number') {\n val = val & 255\n } else if (typeof val === 'boolean') {\n val = Number(val)\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index')\n }\n\n if (end <= start) {\n return this\n }\n\n start = start >>> 0\n end = end === undefined ? this.length : end >>> 0\n\n if (!val) val = 0\n\n let i\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val\n }\n } else {\n const bytes = Buffer.isBuffer(val)\n ? val\n : Buffer.from(val, encoding)\n const len = bytes.length\n if (len === 0) {\n throw new TypeError('The value \"' + val +\n '\" is invalid for argument \"value\"')\n }\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len]\n }\n }\n\n return this\n}\n\n// CUSTOM ERRORS\n// =============\n\n// Simplified versions from Node, changed for Buffer-only usage\nconst errors = {}\nfunction E (sym, getMessage, Base) {\n errors[sym] = class NodeError extends Base {\n constructor () {\n super()\n\n Object.defineProperty(this, 'message', {\n value: getMessage.apply(this, arguments),\n writable: true,\n configurable: true\n })\n\n // Add the error code to the name to include it in the stack trace.\n this.name = `${this.name} [${sym}]`\n // Access the stack to generate the error message including the error code\n // from the name.\n this.stack // eslint-disable-line no-unused-expressions\n // Reset the name to the actual name.\n delete this.name\n }\n\n get code () {\n return sym\n }\n\n set code (value) {\n Object.defineProperty(this, 'code', {\n configurable: true,\n enumerable: true,\n value,\n writable: true\n })\n }\n\n toString () {\n return `${this.name} [${sym}]: ${this.message}`\n }\n }\n}\n\nE('ERR_BUFFER_OUT_OF_BOUNDS',\n function (name) {\n if (name) {\n return `${name} is outside of buffer bounds`\n }\n\n return 'Attempt to access memory outside buffer bounds'\n }, RangeError)\nE('ERR_INVALID_ARG_TYPE',\n function (name, actual) {\n return `The \"${name}\" argument must be of type number. Received type ${typeof actual}`\n }, TypeError)\nE('ERR_OUT_OF_RANGE',\n function (str, range, input) {\n let msg = `The value of \"${str}\" is out of range.`\n let received = input\n if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) {\n received = addNumericalSeparator(String(input))\n } else if (typeof input === 'bigint') {\n received = String(input)\n if (input > BigInt(2) ** BigInt(32) || input < -(BigInt(2) ** BigInt(32))) {\n received = addNumericalSeparator(received)\n }\n received += 'n'\n }\n msg += ` It must be ${range}. Received ${received}`\n return msg\n }, RangeError)\n\nfunction addNumericalSeparator (val) {\n let res = ''\n let i = val.length\n const start = val[0] === '-' ? 1 : 0\n for (; i >= start + 4; i -= 3) {\n res = `_${val.slice(i - 3, i)}${res}`\n }\n return `${val.slice(0, i)}${res}`\n}\n\n// CHECK FUNCTIONS\n// ===============\n\nfunction checkBounds (buf, offset, byteLength) {\n validateNumber(offset, 'offset')\n if (buf[offset] === undefined || buf[offset + byteLength] === undefined) {\n boundsError(offset, buf.length - (byteLength + 1))\n }\n}\n\nfunction checkIntBI (value, min, max, buf, offset, byteLength) {\n if (value > max || value < min) {\n const n = typeof min === 'bigint' ? 'n' : ''\n let range\n if (byteLength > 3) {\n if (min === 0 || min === BigInt(0)) {\n range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`\n } else {\n range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and < 2 ** ` +\n `${(byteLength + 1) * 8 - 1}${n}`\n }\n } else {\n range = `>= ${min}${n} and <= ${max}${n}`\n }\n throw new errors.ERR_OUT_OF_RANGE('value', range, value)\n }\n checkBounds(buf, offset, byteLength)\n}\n\nfunction validateNumber (value, name) {\n if (typeof value !== 'number') {\n throw new errors.ERR_INVALID_ARG_TYPE(name, 'number', value)\n }\n}\n\nfunction boundsError (value, length, type) {\n if (Math.floor(value) !== value) {\n validateNumber(value, type)\n throw new errors.ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value)\n }\n\n if (length < 0) {\n throw new errors.ERR_BUFFER_OUT_OF_BOUNDS()\n }\n\n throw new errors.ERR_OUT_OF_RANGE(type || 'offset',\n `>= ${type ? 1 : 0} and <= ${length}`,\n value)\n}\n\n// HELPER FUNCTIONS\n// ================\n\nconst INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n // Node takes equal signs as end of the Base64 encoding\n str = str.split('=')[0]\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = str.trim().replace(INVALID_BASE64_RE, '')\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return ''\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '='\n }\n return str\n}\n\nfunction utf8ToBytes (string, units) {\n units = units || Infinity\n let codePoint\n const length = string.length\n let leadSurrogate = null\n const bytes = []\n\n for (let i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i)\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n }\n\n // valid lead\n leadSurrogate = codePoint\n\n continue\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n leadSurrogate = codePoint\n continue\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n }\n\n leadSurrogate = null\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break\n bytes.push(codePoint)\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break\n bytes.push(\n codePoint >> 0x6 | 0xC0,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break\n bytes.push(\n codePoint >> 0xC | 0xE0,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break\n bytes.push(\n codePoint >> 0x12 | 0xF0,\n codePoint >> 0xC & 0x3F | 0x80,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else {\n throw new Error('Invalid code point')\n }\n }\n\n return bytes\n}\n\nfunction asciiToBytes (str) {\n const byteArray = []\n for (let i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF)\n }\n return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n let c, hi, lo\n const byteArray = []\n for (let i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break\n\n c = str.charCodeAt(i)\n hi = c >> 8\n lo = c % 256\n byteArray.push(lo)\n byteArray.push(hi)\n }\n\n return byteArray\n}\n\nfunction base64ToBytes (str) {\n return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n let i\n for (i = 0; i < length; ++i) {\n if ((i + offset >= dst.length) || (i >= src.length)) break\n dst[i + offset] = src[i]\n }\n return i\n}\n\n// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass\n// the `instanceof` check but they should be treated as of that type.\n// See: https://github.com/feross/buffer/issues/166\nfunction isInstance (obj, type) {\n return obj instanceof type ||\n (obj != null && obj.constructor != null && obj.constructor.name != null &&\n obj.constructor.name === type.name)\n}\nfunction numberIsNaN (obj) {\n // For IE11 support\n return obj !== obj // eslint-disable-line no-self-compare\n}\n\n// Create lookup table for `toString('hex')`\n// See: https://github.com/feross/buffer/issues/219\nconst hexSliceLookupTable = (function () {\n const alphabet = '0123456789abcdef'\n const table = new Array(256)\n for (let i = 0; i < 16; ++i) {\n const i16 = i * 16\n for (let j = 0; j < 16; ++j) {\n table[i16 + j] = alphabet[i] + alphabet[j]\n }\n }\n return table\n})()\n\n// Return not function with Error if BigInt not supported\nfunction defineBigIntMethod (fn) {\n return typeof BigInt === 'undefined' ? BufferBigIntNotDefined : fn\n}\n\nfunction BufferBigIntNotDefined () {\n throw new Error('BigInt not supported')\n}\n","require('../../modules/es.object.define-property');\nvar path = require('../../internals/path');\n\nvar Object = path.Object;\n\nvar defineProperty = module.exports = function defineProperty(it, key, desc) {\n return Object.defineProperty(it, key, desc);\n};\n\nif (Object.defineProperty.sham) defineProperty.sham = true;\n","var global = require('../internals/global');\nvar isCallable = require('../internals/is-callable');\nvar tryToString = require('../internals/try-to-string');\n\nvar TypeError = global.TypeError;\n\n// `Assert: IsCallable(argument) is true`\nmodule.exports = function (argument) {\n if (isCallable(argument)) return argument;\n throw TypeError(tryToString(argument) + ' is not a function');\n};\n","var global = require('../internals/global');\nvar isObject = require('../internals/is-object');\n\nvar String = global.String;\nvar TypeError = global.TypeError;\n\n// `Assert: Type(argument) is Object`\nmodule.exports = function (argument) {\n if (isObject(argument)) return argument;\n throw TypeError(String(argument) + ' is not an object');\n};\n","var uncurryThis = require('../internals/function-uncurry-this');\n\nvar toString = uncurryThis({}.toString);\nvar stringSlice = uncurryThis(''.slice);\n\nmodule.exports = function (it) {\n return stringSlice(toString(it), 8, -1);\n};\n","var DESCRIPTORS = require('../internals/descriptors');\nvar definePropertyModule = require('../internals/object-define-property');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\n\nmodule.exports = DESCRIPTORS ? function (object, key, value) {\n return definePropertyModule.f(object, key, createPropertyDescriptor(1, value));\n} : function (object, key, value) {\n object[key] = value;\n return object;\n};\n","module.exports = function (bitmap, value) {\n return {\n enumerable: !(bitmap & 1),\n configurable: !(bitmap & 2),\n writable: !(bitmap & 4),\n value: value\n };\n};\n","var fails = require('../internals/fails');\n\n// Detect IE8's incomplete defineProperty implementation\nmodule.exports = !fails(function () {\n // eslint-disable-next-line es/no-object-defineproperty -- required for testing\n return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;\n});\n","var global = require('../internals/global');\nvar isObject = require('../internals/is-object');\n\nvar document = global.document;\n// typeof document.createElement is 'object' in old IE\nvar EXISTS = isObject(document) && isObject(document.createElement);\n\nmodule.exports = function (it) {\n return EXISTS ? document.createElement(it) : {};\n};\n","var getBuiltIn = require('../internals/get-built-in');\n\nmodule.exports = getBuiltIn('navigator', 'userAgent') || '';\n","var global = require('../internals/global');\nvar userAgent = require('../internals/engine-user-agent');\n\nvar process = global.process;\nvar Deno = global.Deno;\nvar versions = process && process.versions || Deno && Deno.version;\nvar v8 = versions && versions.v8;\nvar match, version;\n\nif (v8) {\n match = v8.split('.');\n // in old Chrome, versions of V8 isn't V8 = Chrome / 10\n // but their correct versions are not interesting for us\n version = match[0] > 0 && match[0] < 4 ? 1 : +(match[0] + match[1]);\n}\n\n// BrowserFS NodeJS `process` polyfill incorrectly set `.v8` to `0.0`\n// so check `userAgent` even if `.v8` exists, but 0\nif (!version && userAgent) {\n match = userAgent.match(/Edge\\/(\\d+)/);\n if (!match || match[1] >= 74) {\n match = userAgent.match(/Chrome\\/(\\d+)/);\n if (match) version = +match[1];\n }\n}\n\nmodule.exports = version;\n","'use strict';\nvar global = require('../internals/global');\nvar apply = require('../internals/function-apply');\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar isCallable = require('../internals/is-callable');\nvar getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor').f;\nvar isForced = require('../internals/is-forced');\nvar path = require('../internals/path');\nvar bind = require('../internals/function-bind-context');\nvar createNonEnumerableProperty = require('../internals/create-non-enumerable-property');\nvar hasOwn = require('../internals/has-own-property');\n\nvar wrapConstructor = function (NativeConstructor) {\n var Wrapper = function (a, b, c) {\n if (this instanceof Wrapper) {\n switch (arguments.length) {\n case 0: return new NativeConstructor();\n case 1: return new NativeConstructor(a);\n case 2: return new NativeConstructor(a, b);\n } return new NativeConstructor(a, b, c);\n } return apply(NativeConstructor, this, arguments);\n };\n Wrapper.prototype = NativeConstructor.prototype;\n return Wrapper;\n};\n\n/*\n options.target - name of the target object\n options.global - target is the global object\n options.stat - export as static methods of target\n options.proto - export as prototype methods of target\n options.real - real prototype method for the `pure` version\n options.forced - export even if the native feature is available\n options.bind - bind methods to the target, required for the `pure` version\n options.wrap - wrap constructors to preventing global pollution, required for the `pure` version\n options.unsafe - use the simple assignment of property instead of delete + defineProperty\n options.sham - add a flag to not completely full polyfills\n options.enumerable - export as enumerable property\n options.noTargetGet - prevent calling a getter on target\n options.name - the .name of the function if it does not match the key\n*/\nmodule.exports = function (options, source) {\n var TARGET = options.target;\n var GLOBAL = options.global;\n var STATIC = options.stat;\n var PROTO = options.proto;\n\n var nativeSource = GLOBAL ? global : STATIC ? global[TARGET] : (global[TARGET] || {}).prototype;\n\n var target = GLOBAL ? path : path[TARGET] || createNonEnumerableProperty(path, TARGET, {})[TARGET];\n var targetPrototype = target.prototype;\n\n var FORCED, USE_NATIVE, VIRTUAL_PROTOTYPE;\n var key, sourceProperty, targetProperty, nativeProperty, resultProperty, descriptor;\n\n for (key in source) {\n FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);\n // contains in native\n USE_NATIVE = !FORCED && nativeSource && hasOwn(nativeSource, key);\n\n targetProperty = target[key];\n\n if (USE_NATIVE) if (options.noTargetGet) {\n descriptor = getOwnPropertyDescriptor(nativeSource, key);\n nativeProperty = descriptor && descriptor.value;\n } else nativeProperty = nativeSource[key];\n\n // export native or implementation\n sourceProperty = (USE_NATIVE && nativeProperty) ? nativeProperty : source[key];\n\n if (USE_NATIVE && typeof targetProperty == typeof sourceProperty) continue;\n\n // bind timers to global for call from export context\n if (options.bind && USE_NATIVE) resultProperty = bind(sourceProperty, global);\n // wrap global constructors for prevent changs in this version\n else if (options.wrap && USE_NATIVE) resultProperty = wrapConstructor(sourceProperty);\n // make static versions for prototype methods\n else if (PROTO && isCallable(sourceProperty)) resultProperty = uncurryThis(sourceProperty);\n // default case\n else resultProperty = sourceProperty;\n\n // add a flag to not completely full polyfills\n if (options.sham || (sourceProperty && sourceProperty.sham) || (targetProperty && targetProperty.sham)) {\n createNonEnumerableProperty(resultProperty, 'sham', true);\n }\n\n createNonEnumerableProperty(target, key, resultProperty);\n\n if (PROTO) {\n VIRTUAL_PROTOTYPE = TARGET + 'Prototype';\n if (!hasOwn(path, VIRTUAL_PROTOTYPE)) {\n createNonEnumerableProperty(path, VIRTUAL_PROTOTYPE, {});\n }\n // export virtual prototype methods\n createNonEnumerableProperty(path[VIRTUAL_PROTOTYPE], key, sourceProperty);\n // export real prototype methods\n if (options.real && targetPrototype && !targetPrototype[key]) {\n createNonEnumerableProperty(targetPrototype, key, sourceProperty);\n }\n }\n }\n};\n","module.exports = function (exec) {\n try {\n return !!exec();\n } catch (error) {\n return true;\n }\n};\n","var NATIVE_BIND = require('../internals/function-bind-native');\n\nvar FunctionPrototype = Function.prototype;\nvar apply = FunctionPrototype.apply;\nvar call = FunctionPrototype.call;\n\n// eslint-disable-next-line es/no-reflect -- safe\nmodule.exports = typeof Reflect == 'object' && Reflect.apply || (NATIVE_BIND ? call.bind(apply) : function () {\n return call.apply(apply, arguments);\n});\n","var uncurryThis = require('../internals/function-uncurry-this');\nvar aCallable = require('../internals/a-callable');\nvar NATIVE_BIND = require('../internals/function-bind-native');\n\nvar bind = uncurryThis(uncurryThis.bind);\n\n// optional / simple context binding\nmodule.exports = function (fn, that) {\n aCallable(fn);\n return that === undefined ? fn : NATIVE_BIND ? bind(fn, that) : function (/* ...args */) {\n return fn.apply(that, arguments);\n };\n};\n","var fails = require('../internals/fails');\n\nmodule.exports = !fails(function () {\n var test = (function () { /* empty */ }).bind();\n // eslint-disable-next-line no-prototype-builtins -- safe\n return typeof test != 'function' || test.hasOwnProperty('prototype');\n});\n","var NATIVE_BIND = require('../internals/function-bind-native');\n\nvar call = Function.prototype.call;\n\nmodule.exports = NATIVE_BIND ? call.bind(call) : function () {\n return call.apply(call, arguments);\n};\n","var NATIVE_BIND = require('../internals/function-bind-native');\n\nvar FunctionPrototype = Function.prototype;\nvar bind = FunctionPrototype.bind;\nvar call = FunctionPrototype.call;\nvar uncurryThis = NATIVE_BIND && bind.bind(call, call);\n\nmodule.exports = NATIVE_BIND ? function (fn) {\n return fn && uncurryThis(fn);\n} : function (fn) {\n return fn && function () {\n return call.apply(fn, arguments);\n };\n};\n","var path = require('../internals/path');\nvar global = require('../internals/global');\nvar isCallable = require('../internals/is-callable');\n\nvar aFunction = function (variable) {\n return isCallable(variable) ? variable : undefined;\n};\n\nmodule.exports = function (namespace, method) {\n return arguments.length < 2 ? aFunction(path[namespace]) || aFunction(global[namespace])\n : path[namespace] && path[namespace][method] || global[namespace] && global[namespace][method];\n};\n","var aCallable = require('../internals/a-callable');\n\n// `GetMethod` abstract operation\n// https://tc39.es/ecma262/#sec-getmethod\nmodule.exports = function (V, P) {\n var func = V[P];\n return func == null ? undefined : aCallable(func);\n};\n","var check = function (it) {\n return it && it.Math == Math && it;\n};\n\n// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028\nmodule.exports =\n // eslint-disable-next-line es/no-global-this -- safe\n check(typeof globalThis == 'object' && globalThis) ||\n check(typeof window == 'object' && window) ||\n // eslint-disable-next-line no-restricted-globals -- safe\n check(typeof self == 'object' && self) ||\n check(typeof global == 'object' && global) ||\n // eslint-disable-next-line no-new-func -- fallback\n (function () { return this; })() || Function('return this')();\n","var uncurryThis = require('../internals/function-uncurry-this');\nvar toObject = require('../internals/to-object');\n\nvar hasOwnProperty = uncurryThis({}.hasOwnProperty);\n\n// `HasOwnProperty` abstract operation\n// https://tc39.es/ecma262/#sec-hasownproperty\nmodule.exports = Object.hasOwn || function hasOwn(it, key) {\n return hasOwnProperty(toObject(it), key);\n};\n","var DESCRIPTORS = require('../internals/descriptors');\nvar fails = require('../internals/fails');\nvar createElement = require('../internals/document-create-element');\n\n// Thanks to IE8 for its funny defineProperty\nmodule.exports = !DESCRIPTORS && !fails(function () {\n // eslint-disable-next-line es/no-object-defineproperty -- required for testing\n return Object.defineProperty(createElement('div'), 'a', {\n get: function () { return 7; }\n }).a != 7;\n});\n","var global = require('../internals/global');\nvar uncurryThis = require('../internals/function-uncurry-this');\nvar fails = require('../internals/fails');\nvar classof = require('../internals/classof-raw');\n\nvar Object = global.Object;\nvar split = uncurryThis(''.split);\n\n// fallback for non-array-like ES3 and non-enumerable old V8 strings\nmodule.exports = fails(function () {\n // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346\n // eslint-disable-next-line no-prototype-builtins -- safe\n return !Object('z').propertyIsEnumerable(0);\n}) ? function (it) {\n return classof(it) == 'String' ? split(it, '') : Object(it);\n} : Object;\n","// `IsCallable` abstract operation\n// https://tc39.es/ecma262/#sec-iscallable\nmodule.exports = function (argument) {\n return typeof argument == 'function';\n};\n","var fails = require('../internals/fails');\nvar isCallable = require('../internals/is-callable');\n\nvar replacement = /#|\\.prototype\\./;\n\nvar isForced = function (feature, detection) {\n var value = data[normalize(feature)];\n return value == POLYFILL ? true\n : value == NATIVE ? false\n : isCallable(detection) ? fails(detection)\n : !!detection;\n};\n\nvar normalize = isForced.normalize = function (string) {\n return String(string).replace(replacement, '.').toLowerCase();\n};\n\nvar data = isForced.data = {};\nvar NATIVE = isForced.NATIVE = 'N';\nvar POLYFILL = isForced.POLYFILL = 'P';\n\nmodule.exports = isForced;\n","var isCallable = require('../internals/is-callable');\n\nmodule.exports = function (it) {\n return typeof it == 'object' ? it !== null : isCallable(it);\n};\n","module.exports = true;\n","var global = require('../internals/global');\nvar getBuiltIn = require('../internals/get-built-in');\nvar isCallable = require('../internals/is-callable');\nvar isPrototypeOf = require('../internals/object-is-prototype-of');\nvar USE_SYMBOL_AS_UID = require('../internals/use-symbol-as-uid');\n\nvar Object = global.Object;\n\nmodule.exports = USE_SYMBOL_AS_UID ? function (it) {\n return typeof it == 'symbol';\n} : function (it) {\n var $Symbol = getBuiltIn('Symbol');\n return isCallable($Symbol) && isPrototypeOf($Symbol.prototype, Object(it));\n};\n","/* eslint-disable es/no-symbol -- required for testing */\nvar V8_VERSION = require('../internals/engine-v8-version');\nvar fails = require('../internals/fails');\n\n// eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing\nmodule.exports = !!Object.getOwnPropertySymbols && !fails(function () {\n var symbol = Symbol();\n // Chrome 38 Symbol has incorrect toString conversion\n // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances\n return !String(symbol) || !(Object(symbol) instanceof Symbol) ||\n // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances\n !Symbol.sham && V8_VERSION && V8_VERSION < 41;\n});\n","var global = require('../internals/global');\nvar DESCRIPTORS = require('../internals/descriptors');\nvar IE8_DOM_DEFINE = require('../internals/ie8-dom-define');\nvar V8_PROTOTYPE_DEFINE_BUG = require('../internals/v8-prototype-define-bug');\nvar anObject = require('../internals/an-object');\nvar toPropertyKey = require('../internals/to-property-key');\n\nvar TypeError = global.TypeError;\n// eslint-disable-next-line es/no-object-defineproperty -- safe\nvar $defineProperty = Object.defineProperty;\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\nvar ENUMERABLE = 'enumerable';\nvar CONFIGURABLE = 'configurable';\nvar WRITABLE = 'writable';\n\n// `Object.defineProperty` method\n// https://tc39.es/ecma262/#sec-object.defineproperty\nexports.f = DESCRIPTORS ? V8_PROTOTYPE_DEFINE_BUG ? function defineProperty(O, P, Attributes) {\n anObject(O);\n P = toPropertyKey(P);\n anObject(Attributes);\n if (typeof O === 'function' && P === 'prototype' && 'value' in Attributes && WRITABLE in Attributes && !Attributes[WRITABLE]) {\n var current = $getOwnPropertyDescriptor(O, P);\n if (current && current[WRITABLE]) {\n O[P] = Attributes.value;\n Attributes = {\n configurable: CONFIGURABLE in Attributes ? Attributes[CONFIGURABLE] : current[CONFIGURABLE],\n enumerable: ENUMERABLE in Attributes ? Attributes[ENUMERABLE] : current[ENUMERABLE],\n writable: false\n };\n }\n } return $defineProperty(O, P, Attributes);\n} : $defineProperty : function defineProperty(O, P, Attributes) {\n anObject(O);\n P = toPropertyKey(P);\n anObject(Attributes);\n if (IE8_DOM_DEFINE) try {\n return $defineProperty(O, P, Attributes);\n } catch (error) { /* empty */ }\n if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');\n if ('value' in Attributes) O[P] = Attributes.value;\n return O;\n};\n","var DESCRIPTORS = require('../internals/descriptors');\nvar call = require('../internals/function-call');\nvar propertyIsEnumerableModule = require('../internals/object-property-is-enumerable');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\nvar toIndexedObject = require('../internals/to-indexed-object');\nvar toPropertyKey = require('../internals/to-property-key');\nvar hasOwn = require('../internals/has-own-property');\nvar IE8_DOM_DEFINE = require('../internals/ie8-dom-define');\n\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\n\n// `Object.getOwnPropertyDescriptor` method\n// https://tc39.es/ecma262/#sec-object.getownpropertydescriptor\nexports.f = DESCRIPTORS ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) {\n O = toIndexedObject(O);\n P = toPropertyKey(P);\n if (IE8_DOM_DEFINE) try {\n return $getOwnPropertyDescriptor(O, P);\n } catch (error) { /* empty */ }\n if (hasOwn(O, P)) return createPropertyDescriptor(!call(propertyIsEnumerableModule.f, O, P), O[P]);\n};\n","var uncurryThis = require('../internals/function-uncurry-this');\n\nmodule.exports = uncurryThis({}.isPrototypeOf);\n","'use strict';\nvar $propertyIsEnumerable = {}.propertyIsEnumerable;\n// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe\nvar getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;\n\n// Nashorn ~ JDK8 bug\nvar NASHORN_BUG = getOwnPropertyDescriptor && !$propertyIsEnumerable.call({ 1: 2 }, 1);\n\n// `Object.prototype.propertyIsEnumerable` method implementation\n// https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable\nexports.f = NASHORN_BUG ? function propertyIsEnumerable(V) {\n var descriptor = getOwnPropertyDescriptor(this, V);\n return !!descriptor && descriptor.enumerable;\n} : $propertyIsEnumerable;\n","var global = require('../internals/global');\nvar call = require('../internals/function-call');\nvar isCallable = require('../internals/is-callable');\nvar isObject = require('../internals/is-object');\n\nvar TypeError = global.TypeError;\n\n// `OrdinaryToPrimitive` abstract operation\n// https://tc39.es/ecma262/#sec-ordinarytoprimitive\nmodule.exports = function (input, pref) {\n var fn, val;\n if (pref === 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val;\n if (isCallable(fn = input.valueOf) && !isObject(val = call(fn, input))) return val;\n if (pref !== 'string' && isCallable(fn = input.toString) && !isObject(val = call(fn, input))) return val;\n throw TypeError(\"Can't convert object to primitive value\");\n};\n","module.exports = {};\n","var global = require('../internals/global');\n\nvar TypeError = global.TypeError;\n\n// `RequireObjectCoercible` abstract operation\n// https://tc39.es/ecma262/#sec-requireobjectcoercible\nmodule.exports = function (it) {\n if (it == undefined) throw TypeError(\"Can't call method on \" + it);\n return it;\n};\n","var global = require('../internals/global');\n\n// eslint-disable-next-line es/no-object-defineproperty -- safe\nvar defineProperty = Object.defineProperty;\n\nmodule.exports = function (key, value) {\n try {\n defineProperty(global, key, { value: value, configurable: true, writable: true });\n } catch (error) {\n global[key] = value;\n } return value;\n};\n","var global = require('../internals/global');\nvar setGlobal = require('../internals/set-global');\n\nvar SHARED = '__core-js_shared__';\nvar store = global[SHARED] || setGlobal(SHARED, {});\n\nmodule.exports = store;\n","var IS_PURE = require('../internals/is-pure');\nvar store = require('../internals/shared-store');\n\n(module.exports = function (key, value) {\n return store[key] || (store[key] = value !== undefined ? value : {});\n})('versions', []).push({\n version: '3.20.3',\n mode: IS_PURE ? 'pure' : 'global',\n copyright: '© 2014-2022 Denis Pushkarev (zloirock.ru)',\n license: 'https://github.com/zloirock/core-js/blob/v3.20.3/LICENSE',\n source: 'https://github.com/zloirock/core-js'\n});\n","// toObject with fallback for non-array-like ES3 strings\nvar IndexedObject = require('../internals/indexed-object');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\n\nmodule.exports = function (it) {\n return IndexedObject(requireObjectCoercible(it));\n};\n","var global = require('../internals/global');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\n\nvar Object = global.Object;\n\n// `ToObject` abstract operation\n// https://tc39.es/ecma262/#sec-toobject\nmodule.exports = function (argument) {\n return Object(requireObjectCoercible(argument));\n};\n","var global = require('../internals/global');\nvar call = require('../internals/function-call');\nvar isObject = require('../internals/is-object');\nvar isSymbol = require('../internals/is-symbol');\nvar getMethod = require('../internals/get-method');\nvar ordinaryToPrimitive = require('../internals/ordinary-to-primitive');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\n\nvar TypeError = global.TypeError;\nvar TO_PRIMITIVE = wellKnownSymbol('toPrimitive');\n\n// `ToPrimitive` abstract operation\n// https://tc39.es/ecma262/#sec-toprimitive\nmodule.exports = function (input, pref) {\n if (!isObject(input) || isSymbol(input)) return input;\n var exoticToPrim = getMethod(input, TO_PRIMITIVE);\n var result;\n if (exoticToPrim) {\n if (pref === undefined) pref = 'default';\n result = call(exoticToPrim, input, pref);\n if (!isObject(result) || isSymbol(result)) return result;\n throw TypeError(\"Can't convert object to primitive value\");\n }\n if (pref === undefined) pref = 'number';\n return ordinaryToPrimitive(input, pref);\n};\n","var toPrimitive = require('../internals/to-primitive');\nvar isSymbol = require('../internals/is-symbol');\n\n// `ToPropertyKey` abstract operation\n// https://tc39.es/ecma262/#sec-topropertykey\nmodule.exports = function (argument) {\n var key = toPrimitive(argument, 'string');\n return isSymbol(key) ? key : key + '';\n};\n","var global = require('../internals/global');\n\nvar String = global.String;\n\nmodule.exports = function (argument) {\n try {\n return String(argument);\n } catch (error) {\n return 'Object';\n }\n};\n","var uncurryThis = require('../internals/function-uncurry-this');\n\nvar id = 0;\nvar postfix = Math.random();\nvar toString = uncurryThis(1.0.toString);\n\nmodule.exports = function (key) {\n return 'Symbol(' + (key === undefined ? '' : key) + ')_' + toString(++id + postfix, 36);\n};\n","/* eslint-disable es/no-symbol -- required for testing */\nvar NATIVE_SYMBOL = require('../internals/native-symbol');\n\nmodule.exports = NATIVE_SYMBOL\n && !Symbol.sham\n && typeof Symbol.iterator == 'symbol';\n","var DESCRIPTORS = require('../internals/descriptors');\nvar fails = require('../internals/fails');\n\n// V8 ~ Chrome 36-\n// https://bugs.chromium.org/p/v8/issues/detail?id=3334\nmodule.exports = DESCRIPTORS && fails(function () {\n // eslint-disable-next-line es/no-object-defineproperty -- required for testing\n return Object.defineProperty(function () { /* empty */ }, 'prototype', {\n value: 42,\n writable: false\n }).prototype != 42;\n});\n","var global = require('../internals/global');\nvar shared = require('../internals/shared');\nvar hasOwn = require('../internals/has-own-property');\nvar uid = require('../internals/uid');\nvar NATIVE_SYMBOL = require('../internals/native-symbol');\nvar USE_SYMBOL_AS_UID = require('../internals/use-symbol-as-uid');\n\nvar WellKnownSymbolsStore = shared('wks');\nvar Symbol = global.Symbol;\nvar symbolFor = Symbol && Symbol['for'];\nvar createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol : Symbol && Symbol.withoutSetter || uid;\n\nmodule.exports = function (name) {\n if (!hasOwn(WellKnownSymbolsStore, name) || !(NATIVE_SYMBOL || typeof WellKnownSymbolsStore[name] == 'string')) {\n var description = 'Symbol.' + name;\n if (NATIVE_SYMBOL && hasOwn(Symbol, name)) {\n WellKnownSymbolsStore[name] = Symbol[name];\n } else if (USE_SYMBOL_AS_UID && symbolFor) {\n WellKnownSymbolsStore[name] = symbolFor(description);\n } else {\n WellKnownSymbolsStore[name] = createWellKnownSymbol(description);\n }\n } return WellKnownSymbolsStore[name];\n};\n","var $ = require('../internals/export');\nvar DESCRIPTORS = require('../internals/descriptors');\nvar defineProperty = require('../internals/object-define-property').f;\n\n// `Object.defineProperty` method\n// https://tc39.es/ecma262/#sec-object.defineproperty\n// eslint-disable-next-line es/no-object-defineproperty -- safe\n$({ target: 'Object', stat: true, forced: Object.defineProperty !== defineProperty, sham: !DESCRIPTORS }, {\n defineProperty: defineProperty\n});\n","var parent = require('../../es/object/define-property');\n\nmodule.exports = parent;\n","/*!\n * @description Recursive object extending\n * @author Viacheslav Lotsmanov \n * @license MIT\n *\n * The MIT License (MIT)\n *\n * Copyright (c) 2013-2018 Viacheslav Lotsmanov\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy of\n * this software and associated documentation files (the \"Software\"), to deal in\n * the Software without restriction, including without limitation the rights to\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\n * the Software, and to permit persons to whom the Software is furnished to do so,\n * subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\n * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\n * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n'use strict';\n\nfunction isSpecificValue(val) {\n\treturn (\n\t\tval instanceof Buffer\n\t\t|| val instanceof Date\n\t\t|| val instanceof RegExp\n\t) ? true : false;\n}\n\nfunction cloneSpecificValue(val) {\n\tif (val instanceof Buffer) {\n\t\tvar x = Buffer.alloc\n\t\t\t? Buffer.alloc(val.length)\n\t\t\t: new Buffer(val.length);\n\t\tval.copy(x);\n\t\treturn x;\n\t} else if (val instanceof Date) {\n\t\treturn new Date(val.getTime());\n\t} else if (val instanceof RegExp) {\n\t\treturn new RegExp(val);\n\t} else {\n\t\tthrow new Error('Unexpected situation');\n\t}\n}\n\n/**\n * Recursive cloning array.\n */\nfunction deepCloneArray(arr) {\n\tvar clone = [];\n\tarr.forEach(function (item, index) {\n\t\tif (typeof item === 'object' && item !== null) {\n\t\t\tif (Array.isArray(item)) {\n\t\t\t\tclone[index] = deepCloneArray(item);\n\t\t\t} else if (isSpecificValue(item)) {\n\t\t\t\tclone[index] = cloneSpecificValue(item);\n\t\t\t} else {\n\t\t\t\tclone[index] = deepExtend({}, item);\n\t\t\t}\n\t\t} else {\n\t\t\tclone[index] = item;\n\t\t}\n\t});\n\treturn clone;\n}\n\nfunction safeGetProperty(object, property) {\n\treturn property === '__proto__' ? undefined : object[property];\n}\n\n/**\n * Extening object that entered in first argument.\n *\n * Returns extended object or false if have no target object or incorrect type.\n *\n * If you wish to clone source object (without modify it), just use empty new\n * object as first argument, like this:\n * deepExtend({}, yourObj_1, [yourObj_N]);\n */\nvar deepExtend = module.exports = function (/*obj_1, [obj_2], [obj_N]*/) {\n\tif (arguments.length < 1 || typeof arguments[0] !== 'object') {\n\t\treturn false;\n\t}\n\n\tif (arguments.length < 2) {\n\t\treturn arguments[0];\n\t}\n\n\tvar target = arguments[0];\n\n\t// convert arguments to array and cut off target object\n\tvar args = Array.prototype.slice.call(arguments, 1);\n\n\tvar val, src, clone;\n\n\targs.forEach(function (obj) {\n\t\t// skip argument if isn't an object, is null, or is an array\n\t\tif (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n\t\t\treturn;\n\t\t}\n\n\t\tObject.keys(obj).forEach(function (key) {\n\t\t\tsrc = safeGetProperty(target, key); // source value\n\t\t\tval = safeGetProperty(obj, key); // new value\n\n\t\t\t// recursion prevention\n\t\t\tif (val === target) {\n\t\t\t\treturn;\n\n\t\t\t/**\n\t\t\t * if new value isn't object then just overwrite by new value\n\t\t\t * instead of extending.\n\t\t\t */\n\t\t\t} else if (typeof val !== 'object' || val === null) {\n\t\t\t\ttarget[key] = val;\n\t\t\t\treturn;\n\n\t\t\t// just clone arrays (and recursive clone objects inside)\n\t\t\t} else if (Array.isArray(val)) {\n\t\t\t\ttarget[key] = deepCloneArray(val);\n\t\t\t\treturn;\n\n\t\t\t// custom cloning and overwrite for specific objects\n\t\t\t} else if (isSpecificValue(val)) {\n\t\t\t\ttarget[key] = cloneSpecificValue(val);\n\t\t\t\treturn;\n\n\t\t\t// overwrite by new value if source isn't object or array\n\t\t\t} else if (typeof src !== 'object' || src === null || Array.isArray(src)) {\n\t\t\t\ttarget[key] = deepExtend({}, val);\n\t\t\t\treturn;\n\n\t\t\t// source value and new value is objects both, extending...\n\t\t\t} else {\n\t\t\t\ttarget[key] = deepExtend(src, val);\n\t\t\t\treturn;\n\t\t\t}\n\t\t});\n\t});\n\n\treturn target;\n};\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function errorListener(err) {\n emitter.removeListener(name, resolver);\n reject(err);\n }\n\n function resolver() {\n if (typeof emitter.removeListener === 'function') {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n\n eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });\n if (name !== 'error') {\n addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });\n }\n });\n}\n\nfunction addErrorHandlerIfEventEmitter(emitter, handler, flags) {\n if (typeof emitter.on === 'function') {\n eventTargetAgnosticAddListener(emitter, 'error', handler, flags);\n }\n}\n\nfunction eventTargetAgnosticAddListener(emitter, name, listener, flags) {\n if (typeof emitter.on === 'function') {\n if (flags.once) {\n emitter.once(name, listener);\n } else {\n emitter.on(name, listener);\n }\n } else if (typeof emitter.addEventListener === 'function') {\n // EventTarget does not have `error` event semantics like Node\n // EventEmitters, we do not listen for `error` events here.\n emitter.addEventListener(name, function wrapListener(arg) {\n // IE does not have builtin `{ once: true }` support so we\n // have to do it manually.\n if (flags.once) {\n emitter.removeEventListener(name, wrapListener);\n }\n listener(arg);\n });\n } else {\n throw new TypeError('The \"emitter\" argument must be of type EventEmitter. Received type ' + typeof emitter);\n }\n}\n","if (typeof Object.create === 'function') {\n // implementation from standard node.js 'util' module\n module.exports = function inherits(ctor, superCtor) {\n if (superCtor) {\n ctor.super_ = superCtor\n ctor.prototype = Object.create(superCtor.prototype, {\n constructor: {\n value: ctor,\n enumerable: false,\n writable: true,\n configurable: true\n }\n })\n }\n };\n} else {\n // old school shim for old browsers\n module.exports = function inherits(ctor, superCtor) {\n if (superCtor) {\n ctor.super_ = superCtor\n var TempCtor = function () {}\n TempCtor.prototype = superCtor.prototype\n ctor.prototype = new TempCtor()\n ctor.prototype.constructor = ctor\n }\n }\n}\n","// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n","'use strict'\n\n// limit of Crypto.getRandomValues()\n// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues\nvar MAX_BYTES = 65536\n\n// Node supports requesting up to this number of bytes\n// https://github.com/nodejs/node/blob/master/lib/internal/crypto/random.js#L48\nvar MAX_UINT32 = 4294967295\n\nfunction oldBrowser () {\n throw new Error('Secure random number generation is not supported by this browser.\\nUse Chrome, Firefox or Internet Explorer 11')\n}\n\nvar Buffer = require('safe-buffer').Buffer\nvar crypto = global.crypto || global.msCrypto\n\nif (crypto && crypto.getRandomValues) {\n module.exports = randomBytes\n} else {\n module.exports = oldBrowser\n}\n\nfunction randomBytes (size, cb) {\n // phantomjs needs to throw\n if (size > MAX_UINT32) throw new RangeError('requested too many random bytes')\n\n var bytes = Buffer.allocUnsafe(size)\n\n if (size > 0) { // getRandomValues fails on IE if size == 0\n if (size > MAX_BYTES) { // this is the max bytes crypto.getRandomValues\n // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues\n for (var generated = 0; generated < size; generated += MAX_BYTES) {\n // buffer.slice automatically checks if the end is past the end of\n // the buffer so we don't have to here\n crypto.getRandomValues(bytes.slice(generated, generated + MAX_BYTES))\n }\n } else {\n crypto.getRandomValues(bytes)\n }\n }\n\n if (typeof cb === 'function') {\n return process.nextTick(function () {\n cb(null, bytes)\n })\n }\n\n return bytes\n}\n","'use strict';\n\nfunction _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }\n\nvar codes = {};\n\nfunction createErrorType(code, message, Base) {\n if (!Base) {\n Base = Error;\n }\n\n function getMessage(arg1, arg2, arg3) {\n if (typeof message === 'string') {\n return message;\n } else {\n return message(arg1, arg2, arg3);\n }\n }\n\n var NodeError =\n /*#__PURE__*/\n function (_Base) {\n _inheritsLoose(NodeError, _Base);\n\n function NodeError(arg1, arg2, arg3) {\n return _Base.call(this, getMessage(arg1, arg2, arg3)) || this;\n }\n\n return NodeError;\n }(Base);\n\n NodeError.prototype.name = Base.name;\n NodeError.prototype.code = code;\n codes[code] = NodeError;\n} // https://github.com/nodejs/node/blob/v10.8.0/lib/internal/errors.js\n\n\nfunction oneOf(expected, thing) {\n if (Array.isArray(expected)) {\n var len = expected.length;\n expected = expected.map(function (i) {\n return String(i);\n });\n\n if (len > 2) {\n return \"one of \".concat(thing, \" \").concat(expected.slice(0, len - 1).join(', '), \", or \") + expected[len - 1];\n } else if (len === 2) {\n return \"one of \".concat(thing, \" \").concat(expected[0], \" or \").concat(expected[1]);\n } else {\n return \"of \".concat(thing, \" \").concat(expected[0]);\n }\n } else {\n return \"of \".concat(thing, \" \").concat(String(expected));\n }\n} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith\n\n\nfunction startsWith(str, search, pos) {\n return str.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;\n} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith\n\n\nfunction endsWith(str, search, this_len) {\n if (this_len === undefined || this_len > str.length) {\n this_len = str.length;\n }\n\n return str.substring(this_len - search.length, this_len) === search;\n} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes\n\n\nfunction includes(str, search, start) {\n if (typeof start !== 'number') {\n start = 0;\n }\n\n if (start + search.length > str.length) {\n return false;\n } else {\n return str.indexOf(search, start) !== -1;\n }\n}\n\ncreateErrorType('ERR_INVALID_OPT_VALUE', function (name, value) {\n return 'The value \"' + value + '\" is invalid for option \"' + name + '\"';\n}, TypeError);\ncreateErrorType('ERR_INVALID_ARG_TYPE', function (name, expected, actual) {\n // determiner: 'must be' or 'must not be'\n var determiner;\n\n if (typeof expected === 'string' && startsWith(expected, 'not ')) {\n determiner = 'must not be';\n expected = expected.replace(/^not /, '');\n } else {\n determiner = 'must be';\n }\n\n var msg;\n\n if (endsWith(name, ' argument')) {\n // For cases like 'first argument'\n msg = \"The \".concat(name, \" \").concat(determiner, \" \").concat(oneOf(expected, 'type'));\n } else {\n var type = includes(name, '.') ? 'property' : 'argument';\n msg = \"The \\\"\".concat(name, \"\\\" \").concat(type, \" \").concat(determiner, \" \").concat(oneOf(expected, 'type'));\n }\n\n msg += \". Received type \".concat(typeof actual);\n return msg;\n}, TypeError);\ncreateErrorType('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF');\ncreateErrorType('ERR_METHOD_NOT_IMPLEMENTED', function (name) {\n return 'The ' + name + ' method is not implemented';\n});\ncreateErrorType('ERR_STREAM_PREMATURE_CLOSE', 'Premature close');\ncreateErrorType('ERR_STREAM_DESTROYED', function (name) {\n return 'Cannot call ' + name + ' after a stream was destroyed';\n});\ncreateErrorType('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times');\ncreateErrorType('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable');\ncreateErrorType('ERR_STREAM_WRITE_AFTER_END', 'write after end');\ncreateErrorType('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError);\ncreateErrorType('ERR_UNKNOWN_ENCODING', function (arg) {\n return 'Unknown encoding: ' + arg;\n}, TypeError);\ncreateErrorType('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', 'stream.unshift() after end event');\nmodule.exports.codes = codes;\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n// a duplex stream is just a stream that is both readable and writable.\n// Since JS doesn't have multiple prototypal inheritance, this class\n// prototypally inherits from Readable, and then parasitically from\n// Writable.\n'use strict';\n/**/\n\nvar objectKeys = Object.keys || function (obj) {\n var keys = [];\n\n for (var key in obj) {\n keys.push(key);\n }\n\n return keys;\n};\n/**/\n\n\nmodule.exports = Duplex;\n\nvar Readable = require('./_stream_readable');\n\nvar Writable = require('./_stream_writable');\n\nrequire('inherits')(Duplex, Readable);\n\n{\n // Allow the keys array to be GC'ed.\n var keys = objectKeys(Writable.prototype);\n\n for (var v = 0; v < keys.length; v++) {\n var method = keys[v];\n if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];\n }\n}\n\nfunction Duplex(options) {\n if (!(this instanceof Duplex)) return new Duplex(options);\n Readable.call(this, options);\n Writable.call(this, options);\n this.allowHalfOpen = true;\n\n if (options) {\n if (options.readable === false) this.readable = false;\n if (options.writable === false) this.writable = false;\n\n if (options.allowHalfOpen === false) {\n this.allowHalfOpen = false;\n this.once('end', onend);\n }\n }\n}\n\nObject.defineProperty(Duplex.prototype, 'writableHighWaterMark', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._writableState.highWaterMark;\n }\n});\nObject.defineProperty(Duplex.prototype, 'writableBuffer', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._writableState && this._writableState.getBuffer();\n }\n});\nObject.defineProperty(Duplex.prototype, 'writableLength', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._writableState.length;\n }\n}); // the no-half-open enforcer\n\nfunction onend() {\n // If the writable side ended, then we're ok.\n if (this._writableState.ended) return; // no more data can be written.\n // But allow more writes to happen in this tick.\n\n process.nextTick(onEndNT, this);\n}\n\nfunction onEndNT(self) {\n self.end();\n}\n\nObject.defineProperty(Duplex.prototype, 'destroyed', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n if (this._readableState === undefined || this._writableState === undefined) {\n return false;\n }\n\n return this._readableState.destroyed && this._writableState.destroyed;\n },\n set: function set(value) {\n // we ignore the value if the stream\n // has not been initialized yet\n if (this._readableState === undefined || this._writableState === undefined) {\n return;\n } // backward compatibility, the user is explicitly\n // managing destroyed\n\n\n this._readableState.destroyed = value;\n this._writableState.destroyed = value;\n }\n});","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n// a passthrough stream.\n// basically just the most minimal sort of Transform stream.\n// Every written chunk gets output as-is.\n'use strict';\n\nmodule.exports = PassThrough;\n\nvar Transform = require('./_stream_transform');\n\nrequire('inherits')(PassThrough, Transform);\n\nfunction PassThrough(options) {\n if (!(this instanceof PassThrough)) return new PassThrough(options);\n Transform.call(this, options);\n}\n\nPassThrough.prototype._transform = function (chunk, encoding, cb) {\n cb(null, chunk);\n};","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n'use strict';\n\nmodule.exports = Readable;\n/**/\n\nvar Duplex;\n/**/\n\nReadable.ReadableState = ReadableState;\n/**/\n\nvar EE = require('events').EventEmitter;\n\nvar EElistenerCount = function EElistenerCount(emitter, type) {\n return emitter.listeners(type).length;\n};\n/**/\n\n/**/\n\n\nvar Stream = require('./internal/streams/stream');\n/**/\n\n\nvar Buffer = require('buffer').Buffer;\n\nvar OurUint8Array = global.Uint8Array || function () {};\n\nfunction _uint8ArrayToBuffer(chunk) {\n return Buffer.from(chunk);\n}\n\nfunction _isUint8Array(obj) {\n return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;\n}\n/**/\n\n\nvar debugUtil = require('util');\n\nvar debug;\n\nif (debugUtil && debugUtil.debuglog) {\n debug = debugUtil.debuglog('stream');\n} else {\n debug = function debug() {};\n}\n/**/\n\n\nvar BufferList = require('./internal/streams/buffer_list');\n\nvar destroyImpl = require('./internal/streams/destroy');\n\nvar _require = require('./internal/streams/state'),\n getHighWaterMark = _require.getHighWaterMark;\n\nvar _require$codes = require('../errors').codes,\n ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,\n ERR_STREAM_PUSH_AFTER_EOF = _require$codes.ERR_STREAM_PUSH_AFTER_EOF,\n ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,\n ERR_STREAM_UNSHIFT_AFTER_END_EVENT = _require$codes.ERR_STREAM_UNSHIFT_AFTER_END_EVENT; // Lazy loaded to improve the startup performance.\n\n\nvar StringDecoder;\nvar createReadableStreamAsyncIterator;\nvar from;\n\nrequire('inherits')(Readable, Stream);\n\nvar errorOrDestroy = destroyImpl.errorOrDestroy;\nvar kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume'];\n\nfunction prependListener(emitter, event, fn) {\n // Sadly this is not cacheable as some libraries bundle their own\n // event emitter implementation with them.\n if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any\n // userland ones. NEVER DO THIS. This is here only because this code needs\n // to continue to work with older versions of Node.js that do not include\n // the prependListener() method. The goal is to eventually remove this hack.\n\n if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]];\n}\n\nfunction ReadableState(options, stream, isDuplex) {\n Duplex = Duplex || require('./_stream_duplex');\n options = options || {}; // Duplex streams are both readable and writable, but share\n // the same options object.\n // However, some cases require setting options to different\n // values for the readable and the writable sides of the duplex stream.\n // These options can be provided separately as readableXXX and writableXXX.\n\n if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag. Used to make read(n) ignore n and to\n // make all the buffer merging and length checks go away\n\n this.objectMode = !!options.objectMode;\n if (isDuplex) this.objectMode = this.objectMode || !!options.readableObjectMode; // the point at which it stops calling _read() to fill the buffer\n // Note: 0 is a valid value, means \"don't call _read preemptively ever\"\n\n this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', isDuplex); // A linked list is used to store data chunks instead of an array because the\n // linked list can remove elements from the beginning faster than\n // array.shift()\n\n this.buffer = new BufferList();\n this.length = 0;\n this.pipes = null;\n this.pipesCount = 0;\n this.flowing = null;\n this.ended = false;\n this.endEmitted = false;\n this.reading = false; // a flag to be able to tell if the event 'readable'/'data' is emitted\n // immediately, or on a later tick. We set this to true at first, because\n // any actions that shouldn't happen until \"later\" should generally also\n // not happen before the first read call.\n\n this.sync = true; // whenever we return null, then we set a flag to say\n // that we're awaiting a 'readable' event emission.\n\n this.needReadable = false;\n this.emittedReadable = false;\n this.readableListening = false;\n this.resumeScheduled = false;\n this.paused = true; // Should close be emitted on destroy. Defaults to true.\n\n this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'end' (and potentially 'finish')\n\n this.autoDestroy = !!options.autoDestroy; // has it been destroyed\n\n this.destroyed = false; // Crypto is kind of old and crusty. Historically, its default string\n // encoding is 'binary' so we have to make this configurable.\n // Everything else in the universe uses 'utf8', though.\n\n this.defaultEncoding = options.defaultEncoding || 'utf8'; // the number of writers that are awaiting a drain event in .pipe()s\n\n this.awaitDrain = 0; // if true, a maybeReadMore has been scheduled\n\n this.readingMore = false;\n this.decoder = null;\n this.encoding = null;\n\n if (options.encoding) {\n if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;\n this.decoder = new StringDecoder(options.encoding);\n this.encoding = options.encoding;\n }\n}\n\nfunction Readable(options) {\n Duplex = Duplex || require('./_stream_duplex');\n if (!(this instanceof Readable)) return new Readable(options); // Checking for a Stream.Duplex instance is faster here instead of inside\n // the ReadableState constructor, at least with V8 6.5\n\n var isDuplex = this instanceof Duplex;\n this._readableState = new ReadableState(options, this, isDuplex); // legacy\n\n this.readable = true;\n\n if (options) {\n if (typeof options.read === 'function') this._read = options.read;\n if (typeof options.destroy === 'function') this._destroy = options.destroy;\n }\n\n Stream.call(this);\n}\n\nObject.defineProperty(Readable.prototype, 'destroyed', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n if (this._readableState === undefined) {\n return false;\n }\n\n return this._readableState.destroyed;\n },\n set: function set(value) {\n // we ignore the value if the stream\n // has not been initialized yet\n if (!this._readableState) {\n return;\n } // backward compatibility, the user is explicitly\n // managing destroyed\n\n\n this._readableState.destroyed = value;\n }\n});\nReadable.prototype.destroy = destroyImpl.destroy;\nReadable.prototype._undestroy = destroyImpl.undestroy;\n\nReadable.prototype._destroy = function (err, cb) {\n cb(err);\n}; // Manually shove something into the read() buffer.\n// This returns true if the highWaterMark has not been hit yet,\n// similar to how Writable.write() returns true if you should\n// write() some more.\n\n\nReadable.prototype.push = function (chunk, encoding) {\n var state = this._readableState;\n var skipChunkCheck;\n\n if (!state.objectMode) {\n if (typeof chunk === 'string') {\n encoding = encoding || state.defaultEncoding;\n\n if (encoding !== state.encoding) {\n chunk = Buffer.from(chunk, encoding);\n encoding = '';\n }\n\n skipChunkCheck = true;\n }\n } else {\n skipChunkCheck = true;\n }\n\n return readableAddChunk(this, chunk, encoding, false, skipChunkCheck);\n}; // Unshift should *always* be something directly out of read()\n\n\nReadable.prototype.unshift = function (chunk) {\n return readableAddChunk(this, chunk, null, true, false);\n};\n\nfunction readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) {\n debug('readableAddChunk', chunk);\n var state = stream._readableState;\n\n if (chunk === null) {\n state.reading = false;\n onEofChunk(stream, state);\n } else {\n var er;\n if (!skipChunkCheck) er = chunkInvalid(state, chunk);\n\n if (er) {\n errorOrDestroy(stream, er);\n } else if (state.objectMode || chunk && chunk.length > 0) {\n if (typeof chunk !== 'string' && !state.objectMode && Object.getPrototypeOf(chunk) !== Buffer.prototype) {\n chunk = _uint8ArrayToBuffer(chunk);\n }\n\n if (addToFront) {\n if (state.endEmitted) errorOrDestroy(stream, new ERR_STREAM_UNSHIFT_AFTER_END_EVENT());else addChunk(stream, state, chunk, true);\n } else if (state.ended) {\n errorOrDestroy(stream, new ERR_STREAM_PUSH_AFTER_EOF());\n } else if (state.destroyed) {\n return false;\n } else {\n state.reading = false;\n\n if (state.decoder && !encoding) {\n chunk = state.decoder.write(chunk);\n if (state.objectMode || chunk.length !== 0) addChunk(stream, state, chunk, false);else maybeReadMore(stream, state);\n } else {\n addChunk(stream, state, chunk, false);\n }\n }\n } else if (!addToFront) {\n state.reading = false;\n maybeReadMore(stream, state);\n }\n } // We can push more data if we are below the highWaterMark.\n // Also, if we have no data yet, we can stand some more bytes.\n // This is to work around cases where hwm=0, such as the repl.\n\n\n return !state.ended && (state.length < state.highWaterMark || state.length === 0);\n}\n\nfunction addChunk(stream, state, chunk, addToFront) {\n if (state.flowing && state.length === 0 && !state.sync) {\n state.awaitDrain = 0;\n stream.emit('data', chunk);\n } else {\n // update the buffer info.\n state.length += state.objectMode ? 1 : chunk.length;\n if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);\n if (state.needReadable) emitReadable(stream);\n }\n\n maybeReadMore(stream, state);\n}\n\nfunction chunkInvalid(state, chunk) {\n var er;\n\n if (!_isUint8Array(chunk) && typeof chunk !== 'string' && chunk !== undefined && !state.objectMode) {\n er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer', 'Uint8Array'], chunk);\n }\n\n return er;\n}\n\nReadable.prototype.isPaused = function () {\n return this._readableState.flowing === false;\n}; // backwards compatibility.\n\n\nReadable.prototype.setEncoding = function (enc) {\n if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;\n var decoder = new StringDecoder(enc);\n this._readableState.decoder = decoder; // If setEncoding(null), decoder.encoding equals utf8\n\n this._readableState.encoding = this._readableState.decoder.encoding; // Iterate over current buffer to convert already stored Buffers:\n\n var p = this._readableState.buffer.head;\n var content = '';\n\n while (p !== null) {\n content += decoder.write(p.data);\n p = p.next;\n }\n\n this._readableState.buffer.clear();\n\n if (content !== '') this._readableState.buffer.push(content);\n this._readableState.length = content.length;\n return this;\n}; // Don't raise the hwm > 1GB\n\n\nvar MAX_HWM = 0x40000000;\n\nfunction computeNewHighWaterMark(n) {\n if (n >= MAX_HWM) {\n // TODO(ronag): Throw ERR_VALUE_OUT_OF_RANGE.\n n = MAX_HWM;\n } else {\n // Get the next highest power of 2 to prevent increasing hwm excessively in\n // tiny amounts\n n--;\n n |= n >>> 1;\n n |= n >>> 2;\n n |= n >>> 4;\n n |= n >>> 8;\n n |= n >>> 16;\n n++;\n }\n\n return n;\n} // This function is designed to be inlinable, so please take care when making\n// changes to the function body.\n\n\nfunction howMuchToRead(n, state) {\n if (n <= 0 || state.length === 0 && state.ended) return 0;\n if (state.objectMode) return 1;\n\n if (n !== n) {\n // Only flow one buffer at a time\n if (state.flowing && state.length) return state.buffer.head.data.length;else return state.length;\n } // If we're asking for more than the current hwm, then raise the hwm.\n\n\n if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);\n if (n <= state.length) return n; // Don't have enough\n\n if (!state.ended) {\n state.needReadable = true;\n return 0;\n }\n\n return state.length;\n} // you can override either this method, or the async _read(n) below.\n\n\nReadable.prototype.read = function (n) {\n debug('read', n);\n n = parseInt(n, 10);\n var state = this._readableState;\n var nOrig = n;\n if (n !== 0) state.emittedReadable = false; // if we're doing read(0) to trigger a readable event, but we\n // already have a bunch of data in the buffer, then just trigger\n // the 'readable' event and move on.\n\n if (n === 0 && state.needReadable && ((state.highWaterMark !== 0 ? state.length >= state.highWaterMark : state.length > 0) || state.ended)) {\n debug('read: emitReadable', state.length, state.ended);\n if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);\n return null;\n }\n\n n = howMuchToRead(n, state); // if we've ended, and we're now clear, then finish it up.\n\n if (n === 0 && state.ended) {\n if (state.length === 0) endReadable(this);\n return null;\n } // All the actual chunk generation logic needs to be\n // *below* the call to _read. The reason is that in certain\n // synthetic stream cases, such as passthrough streams, _read\n // may be a completely synchronous operation which may change\n // the state of the read buffer, providing enough data when\n // before there was *not* enough.\n //\n // So, the steps are:\n // 1. Figure out what the state of things will be after we do\n // a read from the buffer.\n //\n // 2. If that resulting state will trigger a _read, then call _read.\n // Note that this may be asynchronous, or synchronous. Yes, it is\n // deeply ugly to write APIs this way, but that still doesn't mean\n // that the Readable class should behave improperly, as streams are\n // designed to be sync/async agnostic.\n // Take note if the _read call is sync or async (ie, if the read call\n // has returned yet), so that we know whether or not it's safe to emit\n // 'readable' etc.\n //\n // 3. Actually pull the requested chunks out of the buffer and return.\n // if we need a readable event, then we need to do some reading.\n\n\n var doRead = state.needReadable;\n debug('need readable', doRead); // if we currently have less than the highWaterMark, then also read some\n\n if (state.length === 0 || state.length - n < state.highWaterMark) {\n doRead = true;\n debug('length less than watermark', doRead);\n } // however, if we've ended, then there's no point, and if we're already\n // reading, then it's unnecessary.\n\n\n if (state.ended || state.reading) {\n doRead = false;\n debug('reading or ended', doRead);\n } else if (doRead) {\n debug('do read');\n state.reading = true;\n state.sync = true; // if the length is currently zero, then we *need* a readable event.\n\n if (state.length === 0) state.needReadable = true; // call internal read method\n\n this._read(state.highWaterMark);\n\n state.sync = false; // If _read pushed data synchronously, then `reading` will be false,\n // and we need to re-evaluate how much data we can return to the user.\n\n if (!state.reading) n = howMuchToRead(nOrig, state);\n }\n\n var ret;\n if (n > 0) ret = fromList(n, state);else ret = null;\n\n if (ret === null) {\n state.needReadable = state.length <= state.highWaterMark;\n n = 0;\n } else {\n state.length -= n;\n state.awaitDrain = 0;\n }\n\n if (state.length === 0) {\n // If we have nothing in the buffer, then we want to know\n // as soon as we *do* get something into the buffer.\n if (!state.ended) state.needReadable = true; // If we tried to read() past the EOF, then emit end on the next tick.\n\n if (nOrig !== n && state.ended) endReadable(this);\n }\n\n if (ret !== null) this.emit('data', ret);\n return ret;\n};\n\nfunction onEofChunk(stream, state) {\n debug('onEofChunk');\n if (state.ended) return;\n\n if (state.decoder) {\n var chunk = state.decoder.end();\n\n if (chunk && chunk.length) {\n state.buffer.push(chunk);\n state.length += state.objectMode ? 1 : chunk.length;\n }\n }\n\n state.ended = true;\n\n if (state.sync) {\n // if we are sync, wait until next tick to emit the data.\n // Otherwise we risk emitting data in the flow()\n // the readable code triggers during a read() call\n emitReadable(stream);\n } else {\n // emit 'readable' now to make sure it gets picked up.\n state.needReadable = false;\n\n if (!state.emittedReadable) {\n state.emittedReadable = true;\n emitReadable_(stream);\n }\n }\n} // Don't emit readable right away in sync mode, because this can trigger\n// another read() call => stack overflow. This way, it might trigger\n// a nextTick recursion warning, but that's not so bad.\n\n\nfunction emitReadable(stream) {\n var state = stream._readableState;\n debug('emitReadable', state.needReadable, state.emittedReadable);\n state.needReadable = false;\n\n if (!state.emittedReadable) {\n debug('emitReadable', state.flowing);\n state.emittedReadable = true;\n process.nextTick(emitReadable_, stream);\n }\n}\n\nfunction emitReadable_(stream) {\n var state = stream._readableState;\n debug('emitReadable_', state.destroyed, state.length, state.ended);\n\n if (!state.destroyed && (state.length || state.ended)) {\n stream.emit('readable');\n state.emittedReadable = false;\n } // The stream needs another readable event if\n // 1. It is not flowing, as the flow mechanism will take\n // care of it.\n // 2. It is not ended.\n // 3. It is below the highWaterMark, so we can schedule\n // another readable later.\n\n\n state.needReadable = !state.flowing && !state.ended && state.length <= state.highWaterMark;\n flow(stream);\n} // at this point, the user has presumably seen the 'readable' event,\n// and called read() to consume some data. that may have triggered\n// in turn another _read(n) call, in which case reading = true if\n// it's in progress.\n// However, if we're not ended, or reading, and the length < hwm,\n// then go ahead and try to read some more preemptively.\n\n\nfunction maybeReadMore(stream, state) {\n if (!state.readingMore) {\n state.readingMore = true;\n process.nextTick(maybeReadMore_, stream, state);\n }\n}\n\nfunction maybeReadMore_(stream, state) {\n // Attempt to read more data if we should.\n //\n // The conditions for reading more data are (one of):\n // - Not enough data buffered (state.length < state.highWaterMark). The loop\n // is responsible for filling the buffer with enough data if such data\n // is available. If highWaterMark is 0 and we are not in the flowing mode\n // we should _not_ attempt to buffer any extra data. We'll get more data\n // when the stream consumer calls read() instead.\n // - No data in the buffer, and the stream is in flowing mode. In this mode\n // the loop below is responsible for ensuring read() is called. Failing to\n // call read here would abort the flow and there's no other mechanism for\n // continuing the flow if the stream consumer has just subscribed to the\n // 'data' event.\n //\n // In addition to the above conditions to keep reading data, the following\n // conditions prevent the data from being read:\n // - The stream has ended (state.ended).\n // - There is already a pending 'read' operation (state.reading). This is a\n // case where the the stream has called the implementation defined _read()\n // method, but they are processing the call asynchronously and have _not_\n // called push() with new data. In this case we skip performing more\n // read()s. The execution ends in this method again after the _read() ends\n // up calling push() with more data.\n while (!state.reading && !state.ended && (state.length < state.highWaterMark || state.flowing && state.length === 0)) {\n var len = state.length;\n debug('maybeReadMore read 0');\n stream.read(0);\n if (len === state.length) // didn't get any data, stop spinning.\n break;\n }\n\n state.readingMore = false;\n} // abstract method. to be overridden in specific implementation classes.\n// call cb(er, data) where data is <= n in length.\n// for virtual (non-string, non-buffer) streams, \"length\" is somewhat\n// arbitrary, and perhaps not very meaningful.\n\n\nReadable.prototype._read = function (n) {\n errorOrDestroy(this, new ERR_METHOD_NOT_IMPLEMENTED('_read()'));\n};\n\nReadable.prototype.pipe = function (dest, pipeOpts) {\n var src = this;\n var state = this._readableState;\n\n switch (state.pipesCount) {\n case 0:\n state.pipes = dest;\n break;\n\n case 1:\n state.pipes = [state.pipes, dest];\n break;\n\n default:\n state.pipes.push(dest);\n break;\n }\n\n state.pipesCount += 1;\n debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);\n var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;\n var endFn = doEnd ? onend : unpipe;\n if (state.endEmitted) process.nextTick(endFn);else src.once('end', endFn);\n dest.on('unpipe', onunpipe);\n\n function onunpipe(readable, unpipeInfo) {\n debug('onunpipe');\n\n if (readable === src) {\n if (unpipeInfo && unpipeInfo.hasUnpiped === false) {\n unpipeInfo.hasUnpiped = true;\n cleanup();\n }\n }\n }\n\n function onend() {\n debug('onend');\n dest.end();\n } // when the dest drains, it reduces the awaitDrain counter\n // on the source. This would be more elegant with a .once()\n // handler in flow(), but adding and removing repeatedly is\n // too slow.\n\n\n var ondrain = pipeOnDrain(src);\n dest.on('drain', ondrain);\n var cleanedUp = false;\n\n function cleanup() {\n debug('cleanup'); // cleanup event handlers once the pipe is broken\n\n dest.removeListener('close', onclose);\n dest.removeListener('finish', onfinish);\n dest.removeListener('drain', ondrain);\n dest.removeListener('error', onerror);\n dest.removeListener('unpipe', onunpipe);\n src.removeListener('end', onend);\n src.removeListener('end', unpipe);\n src.removeListener('data', ondata);\n cleanedUp = true; // if the reader is waiting for a drain event from this\n // specific writer, then it would cause it to never start\n // flowing again.\n // So, if this is awaiting a drain, then we just call it now.\n // If we don't know, then assume that we are waiting for one.\n\n if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();\n }\n\n src.on('data', ondata);\n\n function ondata(chunk) {\n debug('ondata');\n var ret = dest.write(chunk);\n debug('dest.write', ret);\n\n if (ret === false) {\n // If the user unpiped during `dest.write()`, it is possible\n // to get stuck in a permanently paused state if that write\n // also returned false.\n // => Check whether `dest` is still a piping destination.\n if ((state.pipesCount === 1 && state.pipes === dest || state.pipesCount > 1 && indexOf(state.pipes, dest) !== -1) && !cleanedUp) {\n debug('false write response, pause', state.awaitDrain);\n state.awaitDrain++;\n }\n\n src.pause();\n }\n } // if the dest has an error, then stop piping into it.\n // however, don't suppress the throwing behavior for this.\n\n\n function onerror(er) {\n debug('onerror', er);\n unpipe();\n dest.removeListener('error', onerror);\n if (EElistenerCount(dest, 'error') === 0) errorOrDestroy(dest, er);\n } // Make sure our error handler is attached before userland ones.\n\n\n prependListener(dest, 'error', onerror); // Both close and finish should trigger unpipe, but only once.\n\n function onclose() {\n dest.removeListener('finish', onfinish);\n unpipe();\n }\n\n dest.once('close', onclose);\n\n function onfinish() {\n debug('onfinish');\n dest.removeListener('close', onclose);\n unpipe();\n }\n\n dest.once('finish', onfinish);\n\n function unpipe() {\n debug('unpipe');\n src.unpipe(dest);\n } // tell the dest that it's being piped to\n\n\n dest.emit('pipe', src); // start the flow if it hasn't been started already.\n\n if (!state.flowing) {\n debug('pipe resume');\n src.resume();\n }\n\n return dest;\n};\n\nfunction pipeOnDrain(src) {\n return function pipeOnDrainFunctionResult() {\n var state = src._readableState;\n debug('pipeOnDrain', state.awaitDrain);\n if (state.awaitDrain) state.awaitDrain--;\n\n if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {\n state.flowing = true;\n flow(src);\n }\n };\n}\n\nReadable.prototype.unpipe = function (dest) {\n var state = this._readableState;\n var unpipeInfo = {\n hasUnpiped: false\n }; // if we're not piping anywhere, then do nothing.\n\n if (state.pipesCount === 0) return this; // just one destination. most common case.\n\n if (state.pipesCount === 1) {\n // passed in one, but it's not the right one.\n if (dest && dest !== state.pipes) return this;\n if (!dest) dest = state.pipes; // got a match.\n\n state.pipes = null;\n state.pipesCount = 0;\n state.flowing = false;\n if (dest) dest.emit('unpipe', this, unpipeInfo);\n return this;\n } // slow case. multiple pipe destinations.\n\n\n if (!dest) {\n // remove all.\n var dests = state.pipes;\n var len = state.pipesCount;\n state.pipes = null;\n state.pipesCount = 0;\n state.flowing = false;\n\n for (var i = 0; i < len; i++) {\n dests[i].emit('unpipe', this, {\n hasUnpiped: false\n });\n }\n\n return this;\n } // try to find the right one.\n\n\n var index = indexOf(state.pipes, dest);\n if (index === -1) return this;\n state.pipes.splice(index, 1);\n state.pipesCount -= 1;\n if (state.pipesCount === 1) state.pipes = state.pipes[0];\n dest.emit('unpipe', this, unpipeInfo);\n return this;\n}; // set up data events if they are asked for\n// Ensure readable listeners eventually get something\n\n\nReadable.prototype.on = function (ev, fn) {\n var res = Stream.prototype.on.call(this, ev, fn);\n var state = this._readableState;\n\n if (ev === 'data') {\n // update readableListening so that resume() may be a no-op\n // a few lines down. This is needed to support once('readable').\n state.readableListening = this.listenerCount('readable') > 0; // Try start flowing on next tick if stream isn't explicitly paused\n\n if (state.flowing !== false) this.resume();\n } else if (ev === 'readable') {\n if (!state.endEmitted && !state.readableListening) {\n state.readableListening = state.needReadable = true;\n state.flowing = false;\n state.emittedReadable = false;\n debug('on readable', state.length, state.reading);\n\n if (state.length) {\n emitReadable(this);\n } else if (!state.reading) {\n process.nextTick(nReadingNextTick, this);\n }\n }\n }\n\n return res;\n};\n\nReadable.prototype.addListener = Readable.prototype.on;\n\nReadable.prototype.removeListener = function (ev, fn) {\n var res = Stream.prototype.removeListener.call(this, ev, fn);\n\n if (ev === 'readable') {\n // We need to check if there is someone still listening to\n // readable and reset the state. However this needs to happen\n // after readable has been emitted but before I/O (nextTick) to\n // support once('readable', fn) cycles. This means that calling\n // resume within the same tick will have no\n // effect.\n process.nextTick(updateReadableListening, this);\n }\n\n return res;\n};\n\nReadable.prototype.removeAllListeners = function (ev) {\n var res = Stream.prototype.removeAllListeners.apply(this, arguments);\n\n if (ev === 'readable' || ev === undefined) {\n // We need to check if there is someone still listening to\n // readable and reset the state. However this needs to happen\n // after readable has been emitted but before I/O (nextTick) to\n // support once('readable', fn) cycles. This means that calling\n // resume within the same tick will have no\n // effect.\n process.nextTick(updateReadableListening, this);\n }\n\n return res;\n};\n\nfunction updateReadableListening(self) {\n var state = self._readableState;\n state.readableListening = self.listenerCount('readable') > 0;\n\n if (state.resumeScheduled && !state.paused) {\n // flowing needs to be set to true now, otherwise\n // the upcoming resume will not flow.\n state.flowing = true; // crude way to check if we should resume\n } else if (self.listenerCount('data') > 0) {\n self.resume();\n }\n}\n\nfunction nReadingNextTick(self) {\n debug('readable nexttick read 0');\n self.read(0);\n} // pause() and resume() are remnants of the legacy readable stream API\n// If the user uses them, then switch into old mode.\n\n\nReadable.prototype.resume = function () {\n var state = this._readableState;\n\n if (!state.flowing) {\n debug('resume'); // we flow only if there is no one listening\n // for readable, but we still have to call\n // resume()\n\n state.flowing = !state.readableListening;\n resume(this, state);\n }\n\n state.paused = false;\n return this;\n};\n\nfunction resume(stream, state) {\n if (!state.resumeScheduled) {\n state.resumeScheduled = true;\n process.nextTick(resume_, stream, state);\n }\n}\n\nfunction resume_(stream, state) {\n debug('resume', state.reading);\n\n if (!state.reading) {\n stream.read(0);\n }\n\n state.resumeScheduled = false;\n stream.emit('resume');\n flow(stream);\n if (state.flowing && !state.reading) stream.read(0);\n}\n\nReadable.prototype.pause = function () {\n debug('call pause flowing=%j', this._readableState.flowing);\n\n if (this._readableState.flowing !== false) {\n debug('pause');\n this._readableState.flowing = false;\n this.emit('pause');\n }\n\n this._readableState.paused = true;\n return this;\n};\n\nfunction flow(stream) {\n var state = stream._readableState;\n debug('flow', state.flowing);\n\n while (state.flowing && stream.read() !== null) {\n ;\n }\n} // wrap an old-style stream as the async data source.\n// This is *not* part of the readable stream interface.\n// It is an ugly unfortunate mess of history.\n\n\nReadable.prototype.wrap = function (stream) {\n var _this = this;\n\n var state = this._readableState;\n var paused = false;\n stream.on('end', function () {\n debug('wrapped end');\n\n if (state.decoder && !state.ended) {\n var chunk = state.decoder.end();\n if (chunk && chunk.length) _this.push(chunk);\n }\n\n _this.push(null);\n });\n stream.on('data', function (chunk) {\n debug('wrapped data');\n if (state.decoder) chunk = state.decoder.write(chunk); // don't skip over falsy values in objectMode\n\n if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;\n\n var ret = _this.push(chunk);\n\n if (!ret) {\n paused = true;\n stream.pause();\n }\n }); // proxy all the other methods.\n // important when wrapping filters and duplexes.\n\n for (var i in stream) {\n if (this[i] === undefined && typeof stream[i] === 'function') {\n this[i] = function methodWrap(method) {\n return function methodWrapReturnFunction() {\n return stream[method].apply(stream, arguments);\n };\n }(i);\n }\n } // proxy certain important events.\n\n\n for (var n = 0; n < kProxyEvents.length; n++) {\n stream.on(kProxyEvents[n], this.emit.bind(this, kProxyEvents[n]));\n } // when we try to consume some more bytes, simply unpause the\n // underlying stream.\n\n\n this._read = function (n) {\n debug('wrapped _read', n);\n\n if (paused) {\n paused = false;\n stream.resume();\n }\n };\n\n return this;\n};\n\nif (typeof Symbol === 'function') {\n Readable.prototype[Symbol.asyncIterator] = function () {\n if (createReadableStreamAsyncIterator === undefined) {\n createReadableStreamAsyncIterator = require('./internal/streams/async_iterator');\n }\n\n return createReadableStreamAsyncIterator(this);\n };\n}\n\nObject.defineProperty(Readable.prototype, 'readableHighWaterMark', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._readableState.highWaterMark;\n }\n});\nObject.defineProperty(Readable.prototype, 'readableBuffer', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._readableState && this._readableState.buffer;\n }\n});\nObject.defineProperty(Readable.prototype, 'readableFlowing', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._readableState.flowing;\n },\n set: function set(state) {\n if (this._readableState) {\n this._readableState.flowing = state;\n }\n }\n}); // exposed for testing purposes only.\n\nReadable._fromList = fromList;\nObject.defineProperty(Readable.prototype, 'readableLength', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._readableState.length;\n }\n}); // Pluck off n bytes from an array of buffers.\n// Length is the combined lengths of all the buffers in the list.\n// This function is designed to be inlinable, so please take care when making\n// changes to the function body.\n\nfunction fromList(n, state) {\n // nothing buffered\n if (state.length === 0) return null;\n var ret;\n if (state.objectMode) ret = state.buffer.shift();else if (!n || n >= state.length) {\n // read it all, truncate the list\n if (state.decoder) ret = state.buffer.join('');else if (state.buffer.length === 1) ret = state.buffer.first();else ret = state.buffer.concat(state.length);\n state.buffer.clear();\n } else {\n // read part of list\n ret = state.buffer.consume(n, state.decoder);\n }\n return ret;\n}\n\nfunction endReadable(stream) {\n var state = stream._readableState;\n debug('endReadable', state.endEmitted);\n\n if (!state.endEmitted) {\n state.ended = true;\n process.nextTick(endReadableNT, state, stream);\n }\n}\n\nfunction endReadableNT(state, stream) {\n debug('endReadableNT', state.endEmitted, state.length); // Check that we didn't get one last unshift.\n\n if (!state.endEmitted && state.length === 0) {\n state.endEmitted = true;\n stream.readable = false;\n stream.emit('end');\n\n if (state.autoDestroy) {\n // In case of duplex streams we need a way to detect\n // if the writable side is ready for autoDestroy as well\n var wState = stream._writableState;\n\n if (!wState || wState.autoDestroy && wState.finished) {\n stream.destroy();\n }\n }\n }\n}\n\nif (typeof Symbol === 'function') {\n Readable.from = function (iterable, opts) {\n if (from === undefined) {\n from = require('./internal/streams/from');\n }\n\n return from(Readable, iterable, opts);\n };\n}\n\nfunction indexOf(xs, x) {\n for (var i = 0, l = xs.length; i < l; i++) {\n if (xs[i] === x) return i;\n }\n\n return -1;\n}","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n// a transform stream is a readable/writable stream where you do\n// something with the data. Sometimes it's called a \"filter\",\n// but that's not a great name for it, since that implies a thing where\n// some bits pass through, and others are simply ignored. (That would\n// be a valid example of a transform, of course.)\n//\n// While the output is causally related to the input, it's not a\n// necessarily symmetric or synchronous transformation. For example,\n// a zlib stream might take multiple plain-text writes(), and then\n// emit a single compressed chunk some time in the future.\n//\n// Here's how this works:\n//\n// The Transform stream has all the aspects of the readable and writable\n// stream classes. When you write(chunk), that calls _write(chunk,cb)\n// internally, and returns false if there's a lot of pending writes\n// buffered up. When you call read(), that calls _read(n) until\n// there's enough pending readable data buffered up.\n//\n// In a transform stream, the written data is placed in a buffer. When\n// _read(n) is called, it transforms the queued up data, calling the\n// buffered _write cb's as it consumes chunks. If consuming a single\n// written chunk would result in multiple output chunks, then the first\n// outputted bit calls the readcb, and subsequent chunks just go into\n// the read buffer, and will cause it to emit 'readable' if necessary.\n//\n// This way, back-pressure is actually determined by the reading side,\n// since _read has to be called to start processing a new chunk. However,\n// a pathological inflate type of transform can cause excessive buffering\n// here. For example, imagine a stream where every byte of input is\n// interpreted as an integer from 0-255, and then results in that many\n// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in\n// 1kb of data being output. In this case, you could write a very small\n// amount of input, and end up with a very large amount of output. In\n// such a pathological inflating mechanism, there'd be no way to tell\n// the system to stop doing the transform. A single 4MB write could\n// cause the system to run out of memory.\n//\n// However, even in such a pathological case, only a single written chunk\n// would be consumed, and then the rest would wait (un-transformed) until\n// the results of the previous transformed chunk were consumed.\n'use strict';\n\nmodule.exports = Transform;\n\nvar _require$codes = require('../errors').codes,\n ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,\n ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,\n ERR_TRANSFORM_ALREADY_TRANSFORMING = _require$codes.ERR_TRANSFORM_ALREADY_TRANSFORMING,\n ERR_TRANSFORM_WITH_LENGTH_0 = _require$codes.ERR_TRANSFORM_WITH_LENGTH_0;\n\nvar Duplex = require('./_stream_duplex');\n\nrequire('inherits')(Transform, Duplex);\n\nfunction afterTransform(er, data) {\n var ts = this._transformState;\n ts.transforming = false;\n var cb = ts.writecb;\n\n if (cb === null) {\n return this.emit('error', new ERR_MULTIPLE_CALLBACK());\n }\n\n ts.writechunk = null;\n ts.writecb = null;\n if (data != null) // single equals check for both `null` and `undefined`\n this.push(data);\n cb(er);\n var rs = this._readableState;\n rs.reading = false;\n\n if (rs.needReadable || rs.length < rs.highWaterMark) {\n this._read(rs.highWaterMark);\n }\n}\n\nfunction Transform(options) {\n if (!(this instanceof Transform)) return new Transform(options);\n Duplex.call(this, options);\n this._transformState = {\n afterTransform: afterTransform.bind(this),\n needTransform: false,\n transforming: false,\n writecb: null,\n writechunk: null,\n writeencoding: null\n }; // start out asking for a readable event once data is transformed.\n\n this._readableState.needReadable = true; // we have implemented the _read method, and done the other things\n // that Readable wants before the first _read call, so unset the\n // sync guard flag.\n\n this._readableState.sync = false;\n\n if (options) {\n if (typeof options.transform === 'function') this._transform = options.transform;\n if (typeof options.flush === 'function') this._flush = options.flush;\n } // When the writable side finishes, then flush out anything remaining.\n\n\n this.on('prefinish', prefinish);\n}\n\nfunction prefinish() {\n var _this = this;\n\n if (typeof this._flush === 'function' && !this._readableState.destroyed) {\n this._flush(function (er, data) {\n done(_this, er, data);\n });\n } else {\n done(this, null, null);\n }\n}\n\nTransform.prototype.push = function (chunk, encoding) {\n this._transformState.needTransform = false;\n return Duplex.prototype.push.call(this, chunk, encoding);\n}; // This is the part where you do stuff!\n// override this function in implementation classes.\n// 'chunk' is an input chunk.\n//\n// Call `push(newChunk)` to pass along transformed output\n// to the readable side. You may call 'push' zero or more times.\n//\n// Call `cb(err)` when you are done with this chunk. If you pass\n// an error, then that'll put the hurt on the whole operation. If you\n// never call cb(), then you'll never get another chunk.\n\n\nTransform.prototype._transform = function (chunk, encoding, cb) {\n cb(new ERR_METHOD_NOT_IMPLEMENTED('_transform()'));\n};\n\nTransform.prototype._write = function (chunk, encoding, cb) {\n var ts = this._transformState;\n ts.writecb = cb;\n ts.writechunk = chunk;\n ts.writeencoding = encoding;\n\n if (!ts.transforming) {\n var rs = this._readableState;\n if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);\n }\n}; // Doesn't matter what the args are here.\n// _transform does all the work.\n// That we got here means that the readable side wants more data.\n\n\nTransform.prototype._read = function (n) {\n var ts = this._transformState;\n\n if (ts.writechunk !== null && !ts.transforming) {\n ts.transforming = true;\n\n this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);\n } else {\n // mark that we need a transform, so that any data that comes in\n // will get processed, now that we've asked for it.\n ts.needTransform = true;\n }\n};\n\nTransform.prototype._destroy = function (err, cb) {\n Duplex.prototype._destroy.call(this, err, function (err2) {\n cb(err2);\n });\n};\n\nfunction done(stream, er, data) {\n if (er) return stream.emit('error', er);\n if (data != null) // single equals check for both `null` and `undefined`\n stream.push(data); // TODO(BridgeAR): Write a test for these two error cases\n // if there's nothing in the write buffer, then that means\n // that nothing more will ever be provided\n\n if (stream._writableState.length) throw new ERR_TRANSFORM_WITH_LENGTH_0();\n if (stream._transformState.transforming) throw new ERR_TRANSFORM_ALREADY_TRANSFORMING();\n return stream.push(null);\n}","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n// A bit simpler than readable streams.\n// Implement an async ._write(chunk, encoding, cb), and it'll handle all\n// the drain event emission and buffering.\n'use strict';\n\nmodule.exports = Writable;\n/* */\n\nfunction WriteReq(chunk, encoding, cb) {\n this.chunk = chunk;\n this.encoding = encoding;\n this.callback = cb;\n this.next = null;\n} // It seems a linked list but it is not\n// there will be only 2 of these for each stream\n\n\nfunction CorkedRequest(state) {\n var _this = this;\n\n this.next = null;\n this.entry = null;\n\n this.finish = function () {\n onCorkedFinish(_this, state);\n };\n}\n/* */\n\n/**/\n\n\nvar Duplex;\n/**/\n\nWritable.WritableState = WritableState;\n/**/\n\nvar internalUtil = {\n deprecate: require('util-deprecate')\n};\n/**/\n\n/**/\n\nvar Stream = require('./internal/streams/stream');\n/**/\n\n\nvar Buffer = require('buffer').Buffer;\n\nvar OurUint8Array = global.Uint8Array || function () {};\n\nfunction _uint8ArrayToBuffer(chunk) {\n return Buffer.from(chunk);\n}\n\nfunction _isUint8Array(obj) {\n return Buffer.isBuffer(obj) || obj instanceof OurUint8Array;\n}\n\nvar destroyImpl = require('./internal/streams/destroy');\n\nvar _require = require('./internal/streams/state'),\n getHighWaterMark = _require.getHighWaterMark;\n\nvar _require$codes = require('../errors').codes,\n ERR_INVALID_ARG_TYPE = _require$codes.ERR_INVALID_ARG_TYPE,\n ERR_METHOD_NOT_IMPLEMENTED = _require$codes.ERR_METHOD_NOT_IMPLEMENTED,\n ERR_MULTIPLE_CALLBACK = _require$codes.ERR_MULTIPLE_CALLBACK,\n ERR_STREAM_CANNOT_PIPE = _require$codes.ERR_STREAM_CANNOT_PIPE,\n ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED,\n ERR_STREAM_NULL_VALUES = _require$codes.ERR_STREAM_NULL_VALUES,\n ERR_STREAM_WRITE_AFTER_END = _require$codes.ERR_STREAM_WRITE_AFTER_END,\n ERR_UNKNOWN_ENCODING = _require$codes.ERR_UNKNOWN_ENCODING;\n\nvar errorOrDestroy = destroyImpl.errorOrDestroy;\n\nrequire('inherits')(Writable, Stream);\n\nfunction nop() {}\n\nfunction WritableState(options, stream, isDuplex) {\n Duplex = Duplex || require('./_stream_duplex');\n options = options || {}; // Duplex streams are both readable and writable, but share\n // the same options object.\n // However, some cases require setting options to different\n // values for the readable and the writable sides of the duplex stream,\n // e.g. options.readableObjectMode vs. options.writableObjectMode, etc.\n\n if (typeof isDuplex !== 'boolean') isDuplex = stream instanceof Duplex; // object stream flag to indicate whether or not this stream\n // contains buffers or objects.\n\n this.objectMode = !!options.objectMode;\n if (isDuplex) this.objectMode = this.objectMode || !!options.writableObjectMode; // the point at which write() starts returning false\n // Note: 0 is a valid value, means that we always return false if\n // the entire buffer is not flushed immediately on write()\n\n this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', isDuplex); // if _final has been called\n\n this.finalCalled = false; // drain event flag.\n\n this.needDrain = false; // at the start of calling end()\n\n this.ending = false; // when end() has been called, and returned\n\n this.ended = false; // when 'finish' is emitted\n\n this.finished = false; // has it been destroyed\n\n this.destroyed = false; // should we decode strings into buffers before passing to _write?\n // this is here so that some node-core streams can optimize string\n // handling at a lower level.\n\n var noDecode = options.decodeStrings === false;\n this.decodeStrings = !noDecode; // Crypto is kind of old and crusty. Historically, its default string\n // encoding is 'binary' so we have to make this configurable.\n // Everything else in the universe uses 'utf8', though.\n\n this.defaultEncoding = options.defaultEncoding || 'utf8'; // not an actual buffer we keep track of, but a measurement\n // of how much we're waiting to get pushed to some underlying\n // socket or file.\n\n this.length = 0; // a flag to see when we're in the middle of a write.\n\n this.writing = false; // when true all writes will be buffered until .uncork() call\n\n this.corked = 0; // a flag to be able to tell if the onwrite cb is called immediately,\n // or on a later tick. We set this to true at first, because any\n // actions that shouldn't happen until \"later\" should generally also\n // not happen before the first write call.\n\n this.sync = true; // a flag to know if we're processing previously buffered items, which\n // may call the _write() callback in the same tick, so that we don't\n // end up in an overlapped onwrite situation.\n\n this.bufferProcessing = false; // the callback that's passed to _write(chunk,cb)\n\n this.onwrite = function (er) {\n onwrite(stream, er);\n }; // the callback that the user supplies to write(chunk,encoding,cb)\n\n\n this.writecb = null; // the amount that is being written when _write is called.\n\n this.writelen = 0;\n this.bufferedRequest = null;\n this.lastBufferedRequest = null; // number of pending user-supplied write callbacks\n // this must be 0 before 'finish' can be emitted\n\n this.pendingcb = 0; // emit prefinish if the only thing we're waiting for is _write cbs\n // This is relevant for synchronous Transform streams\n\n this.prefinished = false; // True if the error was already emitted and should not be thrown again\n\n this.errorEmitted = false; // Should close be emitted on destroy. Defaults to true.\n\n this.emitClose = options.emitClose !== false; // Should .destroy() be called after 'finish' (and potentially 'end')\n\n this.autoDestroy = !!options.autoDestroy; // count buffered requests\n\n this.bufferedRequestCount = 0; // allocate the first CorkedRequest, there is always\n // one allocated and free to use, and we maintain at most two\n\n this.corkedRequestsFree = new CorkedRequest(this);\n}\n\nWritableState.prototype.getBuffer = function getBuffer() {\n var current = this.bufferedRequest;\n var out = [];\n\n while (current) {\n out.push(current);\n current = current.next;\n }\n\n return out;\n};\n\n(function () {\n try {\n Object.defineProperty(WritableState.prototype, 'buffer', {\n get: internalUtil.deprecate(function writableStateBufferGetter() {\n return this.getBuffer();\n }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.', 'DEP0003')\n });\n } catch (_) {}\n})(); // Test _writableState for inheritance to account for Duplex streams,\n// whose prototype chain only points to Readable.\n\n\nvar realHasInstance;\n\nif (typeof Symbol === 'function' && Symbol.hasInstance && typeof Function.prototype[Symbol.hasInstance] === 'function') {\n realHasInstance = Function.prototype[Symbol.hasInstance];\n Object.defineProperty(Writable, Symbol.hasInstance, {\n value: function value(object) {\n if (realHasInstance.call(this, object)) return true;\n if (this !== Writable) return false;\n return object && object._writableState instanceof WritableState;\n }\n });\n} else {\n realHasInstance = function realHasInstance(object) {\n return object instanceof this;\n };\n}\n\nfunction Writable(options) {\n Duplex = Duplex || require('./_stream_duplex'); // Writable ctor is applied to Duplexes, too.\n // `realHasInstance` is necessary because using plain `instanceof`\n // would return false, as no `_writableState` property is attached.\n // Trying to use the custom `instanceof` for Writable here will also break the\n // Node.js LazyTransform implementation, which has a non-trivial getter for\n // `_writableState` that would lead to infinite recursion.\n // Checking for a Stream.Duplex instance is faster here instead of inside\n // the WritableState constructor, at least with V8 6.5\n\n var isDuplex = this instanceof Duplex;\n if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options);\n this._writableState = new WritableState(options, this, isDuplex); // legacy.\n\n this.writable = true;\n\n if (options) {\n if (typeof options.write === 'function') this._write = options.write;\n if (typeof options.writev === 'function') this._writev = options.writev;\n if (typeof options.destroy === 'function') this._destroy = options.destroy;\n if (typeof options.final === 'function') this._final = options.final;\n }\n\n Stream.call(this);\n} // Otherwise people can pipe Writable streams, which is just wrong.\n\n\nWritable.prototype.pipe = function () {\n errorOrDestroy(this, new ERR_STREAM_CANNOT_PIPE());\n};\n\nfunction writeAfterEnd(stream, cb) {\n var er = new ERR_STREAM_WRITE_AFTER_END(); // TODO: defer error events consistently everywhere, not just the cb\n\n errorOrDestroy(stream, er);\n process.nextTick(cb, er);\n} // Checks that a user-supplied chunk is valid, especially for the particular\n// mode the stream is in. Currently this means that `null` is never accepted\n// and undefined/non-string values are only allowed in object mode.\n\n\nfunction validChunk(stream, state, chunk, cb) {\n var er;\n\n if (chunk === null) {\n er = new ERR_STREAM_NULL_VALUES();\n } else if (typeof chunk !== 'string' && !state.objectMode) {\n er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk);\n }\n\n if (er) {\n errorOrDestroy(stream, er);\n process.nextTick(cb, er);\n return false;\n }\n\n return true;\n}\n\nWritable.prototype.write = function (chunk, encoding, cb) {\n var state = this._writableState;\n var ret = false;\n\n var isBuf = !state.objectMode && _isUint8Array(chunk);\n\n if (isBuf && !Buffer.isBuffer(chunk)) {\n chunk = _uint8ArrayToBuffer(chunk);\n }\n\n if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n if (isBuf) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;\n if (typeof cb !== 'function') cb = nop;\n if (state.ending) writeAfterEnd(this, cb);else if (isBuf || validChunk(this, state, chunk, cb)) {\n state.pendingcb++;\n ret = writeOrBuffer(this, state, isBuf, chunk, encoding, cb);\n }\n return ret;\n};\n\nWritable.prototype.cork = function () {\n this._writableState.corked++;\n};\n\nWritable.prototype.uncork = function () {\n var state = this._writableState;\n\n if (state.corked) {\n state.corked--;\n if (!state.writing && !state.corked && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);\n }\n};\n\nWritable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {\n // node::ParseEncoding() requires lower case.\n if (typeof encoding === 'string') encoding = encoding.toLowerCase();\n if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new ERR_UNKNOWN_ENCODING(encoding);\n this._writableState.defaultEncoding = encoding;\n return this;\n};\n\nObject.defineProperty(Writable.prototype, 'writableBuffer', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._writableState && this._writableState.getBuffer();\n }\n});\n\nfunction decodeChunk(state, chunk, encoding) {\n if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {\n chunk = Buffer.from(chunk, encoding);\n }\n\n return chunk;\n}\n\nObject.defineProperty(Writable.prototype, 'writableHighWaterMark', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._writableState.highWaterMark;\n }\n}); // if we're already writing something, then just put this\n// in the queue, and wait our turn. Otherwise, call _write\n// If we return false, then we need a drain event, so set that flag.\n\nfunction writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {\n if (!isBuf) {\n var newChunk = decodeChunk(state, chunk, encoding);\n\n if (chunk !== newChunk) {\n isBuf = true;\n encoding = 'buffer';\n chunk = newChunk;\n }\n }\n\n var len = state.objectMode ? 1 : chunk.length;\n state.length += len;\n var ret = state.length < state.highWaterMark; // we must ensure that previous needDrain will not be reset to false.\n\n if (!ret) state.needDrain = true;\n\n if (state.writing || state.corked) {\n var last = state.lastBufferedRequest;\n state.lastBufferedRequest = {\n chunk: chunk,\n encoding: encoding,\n isBuf: isBuf,\n callback: cb,\n next: null\n };\n\n if (last) {\n last.next = state.lastBufferedRequest;\n } else {\n state.bufferedRequest = state.lastBufferedRequest;\n }\n\n state.bufferedRequestCount += 1;\n } else {\n doWrite(stream, state, false, len, chunk, encoding, cb);\n }\n\n return ret;\n}\n\nfunction doWrite(stream, state, writev, len, chunk, encoding, cb) {\n state.writelen = len;\n state.writecb = cb;\n state.writing = true;\n state.sync = true;\n if (state.destroyed) state.onwrite(new ERR_STREAM_DESTROYED('write'));else if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);\n state.sync = false;\n}\n\nfunction onwriteError(stream, state, sync, er, cb) {\n --state.pendingcb;\n\n if (sync) {\n // defer the callback if we are being called synchronously\n // to avoid piling up things on the stack\n process.nextTick(cb, er); // this can emit finish, and it will always happen\n // after error\n\n process.nextTick(finishMaybe, stream, state);\n stream._writableState.errorEmitted = true;\n errorOrDestroy(stream, er);\n } else {\n // the caller expect this to happen before if\n // it is async\n cb(er);\n stream._writableState.errorEmitted = true;\n errorOrDestroy(stream, er); // this can emit finish, but finish must\n // always follow error\n\n finishMaybe(stream, state);\n }\n}\n\nfunction onwriteStateUpdate(state) {\n state.writing = false;\n state.writecb = null;\n state.length -= state.writelen;\n state.writelen = 0;\n}\n\nfunction onwrite(stream, er) {\n var state = stream._writableState;\n var sync = state.sync;\n var cb = state.writecb;\n if (typeof cb !== 'function') throw new ERR_MULTIPLE_CALLBACK();\n onwriteStateUpdate(state);\n if (er) onwriteError(stream, state, sync, er, cb);else {\n // Check if we're actually ready to finish, but don't emit yet\n var finished = needFinish(state) || stream.destroyed;\n\n if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {\n clearBuffer(stream, state);\n }\n\n if (sync) {\n process.nextTick(afterWrite, stream, state, finished, cb);\n } else {\n afterWrite(stream, state, finished, cb);\n }\n }\n}\n\nfunction afterWrite(stream, state, finished, cb) {\n if (!finished) onwriteDrain(stream, state);\n state.pendingcb--;\n cb();\n finishMaybe(stream, state);\n} // Must force callback to be called on nextTick, so that we don't\n// emit 'drain' before the write() consumer gets the 'false' return\n// value, and has a chance to attach a 'drain' listener.\n\n\nfunction onwriteDrain(stream, state) {\n if (state.length === 0 && state.needDrain) {\n state.needDrain = false;\n stream.emit('drain');\n }\n} // if there's something in the buffer waiting, then process it\n\n\nfunction clearBuffer(stream, state) {\n state.bufferProcessing = true;\n var entry = state.bufferedRequest;\n\n if (stream._writev && entry && entry.next) {\n // Fast case, write everything using _writev()\n var l = state.bufferedRequestCount;\n var buffer = new Array(l);\n var holder = state.corkedRequestsFree;\n holder.entry = entry;\n var count = 0;\n var allBuffers = true;\n\n while (entry) {\n buffer[count] = entry;\n if (!entry.isBuf) allBuffers = false;\n entry = entry.next;\n count += 1;\n }\n\n buffer.allBuffers = allBuffers;\n doWrite(stream, state, true, state.length, buffer, '', holder.finish); // doWrite is almost always async, defer these to save a bit of time\n // as the hot path ends with doWrite\n\n state.pendingcb++;\n state.lastBufferedRequest = null;\n\n if (holder.next) {\n state.corkedRequestsFree = holder.next;\n holder.next = null;\n } else {\n state.corkedRequestsFree = new CorkedRequest(state);\n }\n\n state.bufferedRequestCount = 0;\n } else {\n // Slow case, write chunks one-by-one\n while (entry) {\n var chunk = entry.chunk;\n var encoding = entry.encoding;\n var cb = entry.callback;\n var len = state.objectMode ? 1 : chunk.length;\n doWrite(stream, state, false, len, chunk, encoding, cb);\n entry = entry.next;\n state.bufferedRequestCount--; // if we didn't call the onwrite immediately, then\n // it means that we need to wait until it does.\n // also, that means that the chunk and cb are currently\n // being processed, so move the buffer counter past them.\n\n if (state.writing) {\n break;\n }\n }\n\n if (entry === null) state.lastBufferedRequest = null;\n }\n\n state.bufferedRequest = entry;\n state.bufferProcessing = false;\n}\n\nWritable.prototype._write = function (chunk, encoding, cb) {\n cb(new ERR_METHOD_NOT_IMPLEMENTED('_write()'));\n};\n\nWritable.prototype._writev = null;\n\nWritable.prototype.end = function (chunk, encoding, cb) {\n var state = this._writableState;\n\n if (typeof chunk === 'function') {\n cb = chunk;\n chunk = null;\n encoding = null;\n } else if (typeof encoding === 'function') {\n cb = encoding;\n encoding = null;\n }\n\n if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); // .end() fully uncorks\n\n if (state.corked) {\n state.corked = 1;\n this.uncork();\n } // ignore unnecessary end() calls.\n\n\n if (!state.ending) endWritable(this, state, cb);\n return this;\n};\n\nObject.defineProperty(Writable.prototype, 'writableLength', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n return this._writableState.length;\n }\n});\n\nfunction needFinish(state) {\n return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;\n}\n\nfunction callFinal(stream, state) {\n stream._final(function (err) {\n state.pendingcb--;\n\n if (err) {\n errorOrDestroy(stream, err);\n }\n\n state.prefinished = true;\n stream.emit('prefinish');\n finishMaybe(stream, state);\n });\n}\n\nfunction prefinish(stream, state) {\n if (!state.prefinished && !state.finalCalled) {\n if (typeof stream._final === 'function' && !state.destroyed) {\n state.pendingcb++;\n state.finalCalled = true;\n process.nextTick(callFinal, stream, state);\n } else {\n state.prefinished = true;\n stream.emit('prefinish');\n }\n }\n}\n\nfunction finishMaybe(stream, state) {\n var need = needFinish(state);\n\n if (need) {\n prefinish(stream, state);\n\n if (state.pendingcb === 0) {\n state.finished = true;\n stream.emit('finish');\n\n if (state.autoDestroy) {\n // In case of duplex streams we need a way to detect\n // if the readable side is ready for autoDestroy as well\n var rState = stream._readableState;\n\n if (!rState || rState.autoDestroy && rState.endEmitted) {\n stream.destroy();\n }\n }\n }\n }\n\n return need;\n}\n\nfunction endWritable(stream, state, cb) {\n state.ending = true;\n finishMaybe(stream, state);\n\n if (cb) {\n if (state.finished) process.nextTick(cb);else stream.once('finish', cb);\n }\n\n state.ended = true;\n stream.writable = false;\n}\n\nfunction onCorkedFinish(corkReq, state, err) {\n var entry = corkReq.entry;\n corkReq.entry = null;\n\n while (entry) {\n var cb = entry.callback;\n state.pendingcb--;\n cb(err);\n entry = entry.next;\n } // reuse the free corkReq.\n\n\n state.corkedRequestsFree.next = corkReq;\n}\n\nObject.defineProperty(Writable.prototype, 'destroyed', {\n // making it explicit this property is not enumerable\n // because otherwise some prototype manipulation in\n // userland will fail\n enumerable: false,\n get: function get() {\n if (this._writableState === undefined) {\n return false;\n }\n\n return this._writableState.destroyed;\n },\n set: function set(value) {\n // we ignore the value if the stream\n // has not been initialized yet\n if (!this._writableState) {\n return;\n } // backward compatibility, the user is explicitly\n // managing destroyed\n\n\n this._writableState.destroyed = value;\n }\n});\nWritable.prototype.destroy = destroyImpl.destroy;\nWritable.prototype._undestroy = destroyImpl.undestroy;\n\nWritable.prototype._destroy = function (err, cb) {\n cb(err);\n};","'use strict';\n\nvar _Object$setPrototypeO;\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nvar finished = require('./end-of-stream');\n\nvar kLastResolve = Symbol('lastResolve');\nvar kLastReject = Symbol('lastReject');\nvar kError = Symbol('error');\nvar kEnded = Symbol('ended');\nvar kLastPromise = Symbol('lastPromise');\nvar kHandlePromise = Symbol('handlePromise');\nvar kStream = Symbol('stream');\n\nfunction createIterResult(value, done) {\n return {\n value: value,\n done: done\n };\n}\n\nfunction readAndResolve(iter) {\n var resolve = iter[kLastResolve];\n\n if (resolve !== null) {\n var data = iter[kStream].read(); // we defer if data is null\n // we can be expecting either 'end' or\n // 'error'\n\n if (data !== null) {\n iter[kLastPromise] = null;\n iter[kLastResolve] = null;\n iter[kLastReject] = null;\n resolve(createIterResult(data, false));\n }\n }\n}\n\nfunction onReadable(iter) {\n // we wait for the next tick, because it might\n // emit an error with process.nextTick\n process.nextTick(readAndResolve, iter);\n}\n\nfunction wrapForNext(lastPromise, iter) {\n return function (resolve, reject) {\n lastPromise.then(function () {\n if (iter[kEnded]) {\n resolve(createIterResult(undefined, true));\n return;\n }\n\n iter[kHandlePromise](resolve, reject);\n }, reject);\n };\n}\n\nvar AsyncIteratorPrototype = Object.getPrototypeOf(function () {});\nvar ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = {\n get stream() {\n return this[kStream];\n },\n\n next: function next() {\n var _this = this;\n\n // if we have detected an error in the meanwhile\n // reject straight away\n var error = this[kError];\n\n if (error !== null) {\n return Promise.reject(error);\n }\n\n if (this[kEnded]) {\n return Promise.resolve(createIterResult(undefined, true));\n }\n\n if (this[kStream].destroyed) {\n // We need to defer via nextTick because if .destroy(err) is\n // called, the error will be emitted via nextTick, and\n // we cannot guarantee that there is no error lingering around\n // waiting to be emitted.\n return new Promise(function (resolve, reject) {\n process.nextTick(function () {\n if (_this[kError]) {\n reject(_this[kError]);\n } else {\n resolve(createIterResult(undefined, true));\n }\n });\n });\n } // if we have multiple next() calls\n // we will wait for the previous Promise to finish\n // this logic is optimized to support for await loops,\n // where next() is only called once at a time\n\n\n var lastPromise = this[kLastPromise];\n var promise;\n\n if (lastPromise) {\n promise = new Promise(wrapForNext(lastPromise, this));\n } else {\n // fast path needed to support multiple this.push()\n // without triggering the next() queue\n var data = this[kStream].read();\n\n if (data !== null) {\n return Promise.resolve(createIterResult(data, false));\n }\n\n promise = new Promise(this[kHandlePromise]);\n }\n\n this[kLastPromise] = promise;\n return promise;\n }\n}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () {\n return this;\n}), _defineProperty(_Object$setPrototypeO, \"return\", function _return() {\n var _this2 = this;\n\n // destroy(err, cb) is a private API\n // we can guarantee we have that here, because we control the\n // Readable class this is attached to\n return new Promise(function (resolve, reject) {\n _this2[kStream].destroy(null, function (err) {\n if (err) {\n reject(err);\n return;\n }\n\n resolve(createIterResult(undefined, true));\n });\n });\n}), _Object$setPrototypeO), AsyncIteratorPrototype);\n\nvar createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {\n var _Object$create;\n\n var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {\n value: stream,\n writable: true\n }), _defineProperty(_Object$create, kLastResolve, {\n value: null,\n writable: true\n }), _defineProperty(_Object$create, kLastReject, {\n value: null,\n writable: true\n }), _defineProperty(_Object$create, kError, {\n value: null,\n writable: true\n }), _defineProperty(_Object$create, kEnded, {\n value: stream._readableState.endEmitted,\n writable: true\n }), _defineProperty(_Object$create, kHandlePromise, {\n value: function value(resolve, reject) {\n var data = iterator[kStream].read();\n\n if (data) {\n iterator[kLastPromise] = null;\n iterator[kLastResolve] = null;\n iterator[kLastReject] = null;\n resolve(createIterResult(data, false));\n } else {\n iterator[kLastResolve] = resolve;\n iterator[kLastReject] = reject;\n }\n },\n writable: true\n }), _Object$create));\n iterator[kLastPromise] = null;\n finished(stream, function (err) {\n if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {\n var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise\n // returned by next() and store the error\n\n if (reject !== null) {\n iterator[kLastPromise] = null;\n iterator[kLastResolve] = null;\n iterator[kLastReject] = null;\n reject(err);\n }\n\n iterator[kError] = err;\n return;\n }\n\n var resolve = iterator[kLastResolve];\n\n if (resolve !== null) {\n iterator[kLastPromise] = null;\n iterator[kLastResolve] = null;\n iterator[kLastReject] = null;\n resolve(createIterResult(undefined, true));\n }\n\n iterator[kEnded] = true;\n });\n stream.on('readable', onReadable.bind(null, iterator));\n return iterator;\n};\n\nmodule.exports = createReadableStreamAsyncIterator;","'use strict';\n\nfunction ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }\n\nfunction _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nvar _require = require('buffer'),\n Buffer = _require.Buffer;\n\nvar _require2 = require('util'),\n inspect = _require2.inspect;\n\nvar custom = inspect && inspect.custom || 'inspect';\n\nfunction copyBuffer(src, target, offset) {\n Buffer.prototype.copy.call(src, target, offset);\n}\n\nmodule.exports =\n/*#__PURE__*/\nfunction () {\n function BufferList() {\n _classCallCheck(this, BufferList);\n\n this.head = null;\n this.tail = null;\n this.length = 0;\n }\n\n _createClass(BufferList, [{\n key: \"push\",\n value: function push(v) {\n var entry = {\n data: v,\n next: null\n };\n if (this.length > 0) this.tail.next = entry;else this.head = entry;\n this.tail = entry;\n ++this.length;\n }\n }, {\n key: \"unshift\",\n value: function unshift(v) {\n var entry = {\n data: v,\n next: this.head\n };\n if (this.length === 0) this.tail = entry;\n this.head = entry;\n ++this.length;\n }\n }, {\n key: \"shift\",\n value: function shift() {\n if (this.length === 0) return;\n var ret = this.head.data;\n if (this.length === 1) this.head = this.tail = null;else this.head = this.head.next;\n --this.length;\n return ret;\n }\n }, {\n key: \"clear\",\n value: function clear() {\n this.head = this.tail = null;\n this.length = 0;\n }\n }, {\n key: \"join\",\n value: function join(s) {\n if (this.length === 0) return '';\n var p = this.head;\n var ret = '' + p.data;\n\n while (p = p.next) {\n ret += s + p.data;\n }\n\n return ret;\n }\n }, {\n key: \"concat\",\n value: function concat(n) {\n if (this.length === 0) return Buffer.alloc(0);\n var ret = Buffer.allocUnsafe(n >>> 0);\n var p = this.head;\n var i = 0;\n\n while (p) {\n copyBuffer(p.data, ret, i);\n i += p.data.length;\n p = p.next;\n }\n\n return ret;\n } // Consumes a specified amount of bytes or characters from the buffered data.\n\n }, {\n key: \"consume\",\n value: function consume(n, hasStrings) {\n var ret;\n\n if (n < this.head.data.length) {\n // `slice` is the same for buffers and strings.\n ret = this.head.data.slice(0, n);\n this.head.data = this.head.data.slice(n);\n } else if (n === this.head.data.length) {\n // First chunk is a perfect match.\n ret = this.shift();\n } else {\n // Result spans more than one buffer.\n ret = hasStrings ? this._getString(n) : this._getBuffer(n);\n }\n\n return ret;\n }\n }, {\n key: \"first\",\n value: function first() {\n return this.head.data;\n } // Consumes a specified amount of characters from the buffered data.\n\n }, {\n key: \"_getString\",\n value: function _getString(n) {\n var p = this.head;\n var c = 1;\n var ret = p.data;\n n -= ret.length;\n\n while (p = p.next) {\n var str = p.data;\n var nb = n > str.length ? str.length : n;\n if (nb === str.length) ret += str;else ret += str.slice(0, n);\n n -= nb;\n\n if (n === 0) {\n if (nb === str.length) {\n ++c;\n if (p.next) this.head = p.next;else this.head = this.tail = null;\n } else {\n this.head = p;\n p.data = str.slice(nb);\n }\n\n break;\n }\n\n ++c;\n }\n\n this.length -= c;\n return ret;\n } // Consumes a specified amount of bytes from the buffered data.\n\n }, {\n key: \"_getBuffer\",\n value: function _getBuffer(n) {\n var ret = Buffer.allocUnsafe(n);\n var p = this.head;\n var c = 1;\n p.data.copy(ret);\n n -= p.data.length;\n\n while (p = p.next) {\n var buf = p.data;\n var nb = n > buf.length ? buf.length : n;\n buf.copy(ret, ret.length - n, 0, nb);\n n -= nb;\n\n if (n === 0) {\n if (nb === buf.length) {\n ++c;\n if (p.next) this.head = p.next;else this.head = this.tail = null;\n } else {\n this.head = p;\n p.data = buf.slice(nb);\n }\n\n break;\n }\n\n ++c;\n }\n\n this.length -= c;\n return ret;\n } // Make sure the linked list only shows the minimal necessary information.\n\n }, {\n key: custom,\n value: function value(_, options) {\n return inspect(this, _objectSpread({}, options, {\n // Only inspect one level.\n depth: 0,\n // It should not recurse.\n customInspect: false\n }));\n }\n }]);\n\n return BufferList;\n}();","'use strict'; // undocumented cb() API, needed for core, not for public API\n\nfunction destroy(err, cb) {\n var _this = this;\n\n var readableDestroyed = this._readableState && this._readableState.destroyed;\n var writableDestroyed = this._writableState && this._writableState.destroyed;\n\n if (readableDestroyed || writableDestroyed) {\n if (cb) {\n cb(err);\n } else if (err) {\n if (!this._writableState) {\n process.nextTick(emitErrorNT, this, err);\n } else if (!this._writableState.errorEmitted) {\n this._writableState.errorEmitted = true;\n process.nextTick(emitErrorNT, this, err);\n }\n }\n\n return this;\n } // we set destroyed to true before firing error callbacks in order\n // to make it re-entrance safe in case destroy() is called within callbacks\n\n\n if (this._readableState) {\n this._readableState.destroyed = true;\n } // if this is a duplex stream mark the writable part as destroyed as well\n\n\n if (this._writableState) {\n this._writableState.destroyed = true;\n }\n\n this._destroy(err || null, function (err) {\n if (!cb && err) {\n if (!_this._writableState) {\n process.nextTick(emitErrorAndCloseNT, _this, err);\n } else if (!_this._writableState.errorEmitted) {\n _this._writableState.errorEmitted = true;\n process.nextTick(emitErrorAndCloseNT, _this, err);\n } else {\n process.nextTick(emitCloseNT, _this);\n }\n } else if (cb) {\n process.nextTick(emitCloseNT, _this);\n cb(err);\n } else {\n process.nextTick(emitCloseNT, _this);\n }\n });\n\n return this;\n}\n\nfunction emitErrorAndCloseNT(self, err) {\n emitErrorNT(self, err);\n emitCloseNT(self);\n}\n\nfunction emitCloseNT(self) {\n if (self._writableState && !self._writableState.emitClose) return;\n if (self._readableState && !self._readableState.emitClose) return;\n self.emit('close');\n}\n\nfunction undestroy() {\n if (this._readableState) {\n this._readableState.destroyed = false;\n this._readableState.reading = false;\n this._readableState.ended = false;\n this._readableState.endEmitted = false;\n }\n\n if (this._writableState) {\n this._writableState.destroyed = false;\n this._writableState.ended = false;\n this._writableState.ending = false;\n this._writableState.finalCalled = false;\n this._writableState.prefinished = false;\n this._writableState.finished = false;\n this._writableState.errorEmitted = false;\n }\n}\n\nfunction emitErrorNT(self, err) {\n self.emit('error', err);\n}\n\nfunction errorOrDestroy(stream, err) {\n // We have tests that rely on errors being emitted\n // in the same tick, so changing this is semver major.\n // For now when you opt-in to autoDestroy we allow\n // the error to be emitted nextTick. In a future\n // semver major update we should change the default to this.\n var rState = stream._readableState;\n var wState = stream._writableState;\n if (rState && rState.autoDestroy || wState && wState.autoDestroy) stream.destroy(err);else stream.emit('error', err);\n}\n\nmodule.exports = {\n destroy: destroy,\n undestroy: undestroy,\n errorOrDestroy: errorOrDestroy\n};","// Ported from https://github.com/mafintosh/end-of-stream with\n// permission from the author, Mathias Buus (@mafintosh).\n'use strict';\n\nvar ERR_STREAM_PREMATURE_CLOSE = require('../../../errors').codes.ERR_STREAM_PREMATURE_CLOSE;\n\nfunction once(callback) {\n var called = false;\n return function () {\n if (called) return;\n called = true;\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n callback.apply(this, args);\n };\n}\n\nfunction noop() {}\n\nfunction isRequest(stream) {\n return stream.setHeader && typeof stream.abort === 'function';\n}\n\nfunction eos(stream, opts, callback) {\n if (typeof opts === 'function') return eos(stream, null, opts);\n if (!opts) opts = {};\n callback = once(callback || noop);\n var readable = opts.readable || opts.readable !== false && stream.readable;\n var writable = opts.writable || opts.writable !== false && stream.writable;\n\n var onlegacyfinish = function onlegacyfinish() {\n if (!stream.writable) onfinish();\n };\n\n var writableEnded = stream._writableState && stream._writableState.finished;\n\n var onfinish = function onfinish() {\n writable = false;\n writableEnded = true;\n if (!readable) callback.call(stream);\n };\n\n var readableEnded = stream._readableState && stream._readableState.endEmitted;\n\n var onend = function onend() {\n readable = false;\n readableEnded = true;\n if (!writable) callback.call(stream);\n };\n\n var onerror = function onerror(err) {\n callback.call(stream, err);\n };\n\n var onclose = function onclose() {\n var err;\n\n if (readable && !readableEnded) {\n if (!stream._readableState || !stream._readableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE();\n return callback.call(stream, err);\n }\n\n if (writable && !writableEnded) {\n if (!stream._writableState || !stream._writableState.ended) err = new ERR_STREAM_PREMATURE_CLOSE();\n return callback.call(stream, err);\n }\n };\n\n var onrequest = function onrequest() {\n stream.req.on('finish', onfinish);\n };\n\n if (isRequest(stream)) {\n stream.on('complete', onfinish);\n stream.on('abort', onclose);\n if (stream.req) onrequest();else stream.on('request', onrequest);\n } else if (writable && !stream._writableState) {\n // legacy streams\n stream.on('end', onlegacyfinish);\n stream.on('close', onlegacyfinish);\n }\n\n stream.on('end', onend);\n stream.on('finish', onfinish);\n if (opts.error !== false) stream.on('error', onerror);\n stream.on('close', onclose);\n return function () {\n stream.removeListener('complete', onfinish);\n stream.removeListener('abort', onclose);\n stream.removeListener('request', onrequest);\n if (stream.req) stream.req.removeListener('finish', onfinish);\n stream.removeListener('end', onlegacyfinish);\n stream.removeListener('close', onlegacyfinish);\n stream.removeListener('finish', onfinish);\n stream.removeListener('end', onend);\n stream.removeListener('error', onerror);\n stream.removeListener('close', onclose);\n };\n}\n\nmodule.exports = eos;","module.exports = function () {\n throw new Error('Readable.from is not available in the browser')\n};\n","// Ported from https://github.com/mafintosh/pump with\n// permission from the author, Mathias Buus (@mafintosh).\n'use strict';\n\nvar eos;\n\nfunction once(callback) {\n var called = false;\n return function () {\n if (called) return;\n called = true;\n callback.apply(void 0, arguments);\n };\n}\n\nvar _require$codes = require('../../../errors').codes,\n ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS,\n ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED;\n\nfunction noop(err) {\n // Rethrow the error if it exists to avoid swallowing it\n if (err) throw err;\n}\n\nfunction isRequest(stream) {\n return stream.setHeader && typeof stream.abort === 'function';\n}\n\nfunction destroyer(stream, reading, writing, callback) {\n callback = once(callback);\n var closed = false;\n stream.on('close', function () {\n closed = true;\n });\n if (eos === undefined) eos = require('./end-of-stream');\n eos(stream, {\n readable: reading,\n writable: writing\n }, function (err) {\n if (err) return callback(err);\n closed = true;\n callback();\n });\n var destroyed = false;\n return function (err) {\n if (closed) return;\n if (destroyed) return;\n destroyed = true; // request.destroy just do .end - .abort is what we want\n\n if (isRequest(stream)) return stream.abort();\n if (typeof stream.destroy === 'function') return stream.destroy();\n callback(err || new ERR_STREAM_DESTROYED('pipe'));\n };\n}\n\nfunction call(fn) {\n fn();\n}\n\nfunction pipe(from, to) {\n return from.pipe(to);\n}\n\nfunction popCallback(streams) {\n if (!streams.length) return noop;\n if (typeof streams[streams.length - 1] !== 'function') return noop;\n return streams.pop();\n}\n\nfunction pipeline() {\n for (var _len = arguments.length, streams = new Array(_len), _key = 0; _key < _len; _key++) {\n streams[_key] = arguments[_key];\n }\n\n var callback = popCallback(streams);\n if (Array.isArray(streams[0])) streams = streams[0];\n\n if (streams.length < 2) {\n throw new ERR_MISSING_ARGS('streams');\n }\n\n var error;\n var destroys = streams.map(function (stream, i) {\n var reading = i < streams.length - 1;\n var writing = i > 0;\n return destroyer(stream, reading, writing, function (err) {\n if (!error) error = err;\n if (err) destroys.forEach(call);\n if (reading) return;\n destroys.forEach(call);\n callback(error);\n });\n });\n return streams.reduce(pipe);\n}\n\nmodule.exports = pipeline;","'use strict';\n\nvar ERR_INVALID_OPT_VALUE = require('../../../errors').codes.ERR_INVALID_OPT_VALUE;\n\nfunction highWaterMarkFrom(options, isDuplex, duplexKey) {\n return options.highWaterMark != null ? options.highWaterMark : isDuplex ? options[duplexKey] : null;\n}\n\nfunction getHighWaterMark(state, options, duplexKey, isDuplex) {\n var hwm = highWaterMarkFrom(options, isDuplex, duplexKey);\n\n if (hwm != null) {\n if (!(isFinite(hwm) && Math.floor(hwm) === hwm) || hwm < 0) {\n var name = isDuplex ? duplexKey : 'highWaterMark';\n throw new ERR_INVALID_OPT_VALUE(name, hwm);\n }\n\n return Math.floor(hwm);\n } // Default value\n\n\n return state.objectMode ? 16 : 16 * 1024;\n}\n\nmodule.exports = {\n getHighWaterMark: getHighWaterMark\n};","module.exports = require('events').EventEmitter;\n","var Buffer = require('safe-buffer').Buffer\n\n// prototype class for hash functions\nfunction Hash (blockSize, finalSize) {\n this._block = Buffer.alloc(blockSize)\n this._finalSize = finalSize\n this._blockSize = blockSize\n this._len = 0\n}\n\nHash.prototype.update = function (data, enc) {\n if (typeof data === 'string') {\n enc = enc || 'utf8'\n data = Buffer.from(data, enc)\n }\n\n var block = this._block\n var blockSize = this._blockSize\n var length = data.length\n var accum = this._len\n\n for (var offset = 0; offset < length;) {\n var assigned = accum % blockSize\n var remainder = Math.min(length - offset, blockSize - assigned)\n\n for (var i = 0; i < remainder; i++) {\n block[assigned + i] = data[offset + i]\n }\n\n accum += remainder\n offset += remainder\n\n if ((accum % blockSize) === 0) {\n this._update(block)\n }\n }\n\n this._len += length\n return this\n}\n\nHash.prototype.digest = function (enc) {\n var rem = this._len % this._blockSize\n\n this._block[rem] = 0x80\n\n // zero (rem + 1) trailing bits, where (rem + 1) is the smallest\n // non-negative solution to the equation (length + 1 + (rem + 1)) === finalSize mod blockSize\n this._block.fill(0, rem + 1)\n\n if (rem >= this._finalSize) {\n this._update(this._block)\n this._block.fill(0)\n }\n\n var bits = this._len * 8\n\n // uint32\n if (bits <= 0xffffffff) {\n this._block.writeUInt32BE(bits, this._blockSize - 4)\n\n // uint64\n } else {\n var lowBits = (bits & 0xffffffff) >>> 0\n var highBits = (bits - lowBits) / 0x100000000\n\n this._block.writeUInt32BE(highBits, this._blockSize - 8)\n this._block.writeUInt32BE(lowBits, this._blockSize - 4)\n }\n\n this._update(this._block)\n var hash = this._hash()\n\n return enc ? hash.toString(enc) : hash\n}\n\nHash.prototype._update = function () {\n throw new Error('_update must be implemented by subclass')\n}\n\nmodule.exports = Hash\n","var exports = module.exports = function SHA (algorithm) {\n algorithm = algorithm.toLowerCase()\n\n var Algorithm = exports[algorithm]\n if (!Algorithm) throw new Error(algorithm + ' is not supported (we accept pull requests)')\n\n return new Algorithm()\n}\n\nexports.sha = require('./sha')\nexports.sha1 = require('./sha1')\nexports.sha224 = require('./sha224')\nexports.sha256 = require('./sha256')\nexports.sha384 = require('./sha384')\nexports.sha512 = require('./sha512')\n","/*\n * A JavaScript implementation of the Secure Hash Algorithm, SHA-0, as defined\n * in FIPS PUB 180-1\n * This source code is derived from sha1.js of the same repository.\n * The difference between SHA-0 and SHA-1 is just a bitwise rotate left\n * operation was added.\n */\n\nvar inherits = require('inherits')\nvar Hash = require('./hash')\nvar Buffer = require('safe-buffer').Buffer\n\nvar K = [\n 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc | 0, 0xca62c1d6 | 0\n]\n\nvar W = new Array(80)\n\nfunction Sha () {\n this.init()\n this._w = W\n\n Hash.call(this, 64, 56)\n}\n\ninherits(Sha, Hash)\n\nSha.prototype.init = function () {\n this._a = 0x67452301\n this._b = 0xefcdab89\n this._c = 0x98badcfe\n this._d = 0x10325476\n this._e = 0xc3d2e1f0\n\n return this\n}\n\nfunction rotl5 (num) {\n return (num << 5) | (num >>> 27)\n}\n\nfunction rotl30 (num) {\n return (num << 30) | (num >>> 2)\n}\n\nfunction ft (s, b, c, d) {\n if (s === 0) return (b & c) | ((~b) & d)\n if (s === 2) return (b & c) | (b & d) | (c & d)\n return b ^ c ^ d\n}\n\nSha.prototype._update = function (M) {\n var W = this._w\n\n var a = this._a | 0\n var b = this._b | 0\n var c = this._c | 0\n var d = this._d | 0\n var e = this._e | 0\n\n for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4)\n for (; i < 80; ++i) W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]\n\n for (var j = 0; j < 80; ++j) {\n var s = ~~(j / 20)\n var t = (rotl5(a) + ft(s, b, c, d) + e + W[j] + K[s]) | 0\n\n e = d\n d = c\n c = rotl30(b)\n b = a\n a = t\n }\n\n this._a = (a + this._a) | 0\n this._b = (b + this._b) | 0\n this._c = (c + this._c) | 0\n this._d = (d + this._d) | 0\n this._e = (e + this._e) | 0\n}\n\nSha.prototype._hash = function () {\n var H = Buffer.allocUnsafe(20)\n\n H.writeInt32BE(this._a | 0, 0)\n H.writeInt32BE(this._b | 0, 4)\n H.writeInt32BE(this._c | 0, 8)\n H.writeInt32BE(this._d | 0, 12)\n H.writeInt32BE(this._e | 0, 16)\n\n return H\n}\n\nmodule.exports = Sha\n","/*\n * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined\n * in FIPS PUB 180-1\n * Version 2.1a Copyright Paul Johnston 2000 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for details.\n */\n\nvar inherits = require('inherits')\nvar Hash = require('./hash')\nvar Buffer = require('safe-buffer').Buffer\n\nvar K = [\n 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc | 0, 0xca62c1d6 | 0\n]\n\nvar W = new Array(80)\n\nfunction Sha1 () {\n this.init()\n this._w = W\n\n Hash.call(this, 64, 56)\n}\n\ninherits(Sha1, Hash)\n\nSha1.prototype.init = function () {\n this._a = 0x67452301\n this._b = 0xefcdab89\n this._c = 0x98badcfe\n this._d = 0x10325476\n this._e = 0xc3d2e1f0\n\n return this\n}\n\nfunction rotl1 (num) {\n return (num << 1) | (num >>> 31)\n}\n\nfunction rotl5 (num) {\n return (num << 5) | (num >>> 27)\n}\n\nfunction rotl30 (num) {\n return (num << 30) | (num >>> 2)\n}\n\nfunction ft (s, b, c, d) {\n if (s === 0) return (b & c) | ((~b) & d)\n if (s === 2) return (b & c) | (b & d) | (c & d)\n return b ^ c ^ d\n}\n\nSha1.prototype._update = function (M) {\n var W = this._w\n\n var a = this._a | 0\n var b = this._b | 0\n var c = this._c | 0\n var d = this._d | 0\n var e = this._e | 0\n\n for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4)\n for (; i < 80; ++i) W[i] = rotl1(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16])\n\n for (var j = 0; j < 80; ++j) {\n var s = ~~(j / 20)\n var t = (rotl5(a) + ft(s, b, c, d) + e + W[j] + K[s]) | 0\n\n e = d\n d = c\n c = rotl30(b)\n b = a\n a = t\n }\n\n this._a = (a + this._a) | 0\n this._b = (b + this._b) | 0\n this._c = (c + this._c) | 0\n this._d = (d + this._d) | 0\n this._e = (e + this._e) | 0\n}\n\nSha1.prototype._hash = function () {\n var H = Buffer.allocUnsafe(20)\n\n H.writeInt32BE(this._a | 0, 0)\n H.writeInt32BE(this._b | 0, 4)\n H.writeInt32BE(this._c | 0, 8)\n H.writeInt32BE(this._d | 0, 12)\n H.writeInt32BE(this._e | 0, 16)\n\n return H\n}\n\nmodule.exports = Sha1\n","/**\n * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined\n * in FIPS 180-2\n * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n *\n */\n\nvar inherits = require('inherits')\nvar Sha256 = require('./sha256')\nvar Hash = require('./hash')\nvar Buffer = require('safe-buffer').Buffer\n\nvar W = new Array(64)\n\nfunction Sha224 () {\n this.init()\n\n this._w = W // new Array(64)\n\n Hash.call(this, 64, 56)\n}\n\ninherits(Sha224, Sha256)\n\nSha224.prototype.init = function () {\n this._a = 0xc1059ed8\n this._b = 0x367cd507\n this._c = 0x3070dd17\n this._d = 0xf70e5939\n this._e = 0xffc00b31\n this._f = 0x68581511\n this._g = 0x64f98fa7\n this._h = 0xbefa4fa4\n\n return this\n}\n\nSha224.prototype._hash = function () {\n var H = Buffer.allocUnsafe(28)\n\n H.writeInt32BE(this._a, 0)\n H.writeInt32BE(this._b, 4)\n H.writeInt32BE(this._c, 8)\n H.writeInt32BE(this._d, 12)\n H.writeInt32BE(this._e, 16)\n H.writeInt32BE(this._f, 20)\n H.writeInt32BE(this._g, 24)\n\n return H\n}\n\nmodule.exports = Sha224\n","/**\n * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined\n * in FIPS 180-2\n * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n *\n */\n\nvar inherits = require('inherits')\nvar Hash = require('./hash')\nvar Buffer = require('safe-buffer').Buffer\n\nvar K = [\n 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,\n 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,\n 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,\n 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,\n 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,\n 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,\n 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,\n 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,\n 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,\n 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,\n 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,\n 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,\n 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,\n 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,\n 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,\n 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2\n]\n\nvar W = new Array(64)\n\nfunction Sha256 () {\n this.init()\n\n this._w = W // new Array(64)\n\n Hash.call(this, 64, 56)\n}\n\ninherits(Sha256, Hash)\n\nSha256.prototype.init = function () {\n this._a = 0x6a09e667\n this._b = 0xbb67ae85\n this._c = 0x3c6ef372\n this._d = 0xa54ff53a\n this._e = 0x510e527f\n this._f = 0x9b05688c\n this._g = 0x1f83d9ab\n this._h = 0x5be0cd19\n\n return this\n}\n\nfunction ch (x, y, z) {\n return z ^ (x & (y ^ z))\n}\n\nfunction maj (x, y, z) {\n return (x & y) | (z & (x | y))\n}\n\nfunction sigma0 (x) {\n return (x >>> 2 | x << 30) ^ (x >>> 13 | x << 19) ^ (x >>> 22 | x << 10)\n}\n\nfunction sigma1 (x) {\n return (x >>> 6 | x << 26) ^ (x >>> 11 | x << 21) ^ (x >>> 25 | x << 7)\n}\n\nfunction gamma0 (x) {\n return (x >>> 7 | x << 25) ^ (x >>> 18 | x << 14) ^ (x >>> 3)\n}\n\nfunction gamma1 (x) {\n return (x >>> 17 | x << 15) ^ (x >>> 19 | x << 13) ^ (x >>> 10)\n}\n\nSha256.prototype._update = function (M) {\n var W = this._w\n\n var a = this._a | 0\n var b = this._b | 0\n var c = this._c | 0\n var d = this._d | 0\n var e = this._e | 0\n var f = this._f | 0\n var g = this._g | 0\n var h = this._h | 0\n\n for (var i = 0; i < 16; ++i) W[i] = M.readInt32BE(i * 4)\n for (; i < 64; ++i) W[i] = (gamma1(W[i - 2]) + W[i - 7] + gamma0(W[i - 15]) + W[i - 16]) | 0\n\n for (var j = 0; j < 64; ++j) {\n var T1 = (h + sigma1(e) + ch(e, f, g) + K[j] + W[j]) | 0\n var T2 = (sigma0(a) + maj(a, b, c)) | 0\n\n h = g\n g = f\n f = e\n e = (d + T1) | 0\n d = c\n c = b\n b = a\n a = (T1 + T2) | 0\n }\n\n this._a = (a + this._a) | 0\n this._b = (b + this._b) | 0\n this._c = (c + this._c) | 0\n this._d = (d + this._d) | 0\n this._e = (e + this._e) | 0\n this._f = (f + this._f) | 0\n this._g = (g + this._g) | 0\n this._h = (h + this._h) | 0\n}\n\nSha256.prototype._hash = function () {\n var H = Buffer.allocUnsafe(32)\n\n H.writeInt32BE(this._a, 0)\n H.writeInt32BE(this._b, 4)\n H.writeInt32BE(this._c, 8)\n H.writeInt32BE(this._d, 12)\n H.writeInt32BE(this._e, 16)\n H.writeInt32BE(this._f, 20)\n H.writeInt32BE(this._g, 24)\n H.writeInt32BE(this._h, 28)\n\n return H\n}\n\nmodule.exports = Sha256\n","var inherits = require('inherits')\nvar SHA512 = require('./sha512')\nvar Hash = require('./hash')\nvar Buffer = require('safe-buffer').Buffer\n\nvar W = new Array(160)\n\nfunction Sha384 () {\n this.init()\n this._w = W\n\n Hash.call(this, 128, 112)\n}\n\ninherits(Sha384, SHA512)\n\nSha384.prototype.init = function () {\n this._ah = 0xcbbb9d5d\n this._bh = 0x629a292a\n this._ch = 0x9159015a\n this._dh = 0x152fecd8\n this._eh = 0x67332667\n this._fh = 0x8eb44a87\n this._gh = 0xdb0c2e0d\n this._hh = 0x47b5481d\n\n this._al = 0xc1059ed8\n this._bl = 0x367cd507\n this._cl = 0x3070dd17\n this._dl = 0xf70e5939\n this._el = 0xffc00b31\n this._fl = 0x68581511\n this._gl = 0x64f98fa7\n this._hl = 0xbefa4fa4\n\n return this\n}\n\nSha384.prototype._hash = function () {\n var H = Buffer.allocUnsafe(48)\n\n function writeInt64BE (h, l, offset) {\n H.writeInt32BE(h, offset)\n H.writeInt32BE(l, offset + 4)\n }\n\n writeInt64BE(this._ah, this._al, 0)\n writeInt64BE(this._bh, this._bl, 8)\n writeInt64BE(this._ch, this._cl, 16)\n writeInt64BE(this._dh, this._dl, 24)\n writeInt64BE(this._eh, this._el, 32)\n writeInt64BE(this._fh, this._fl, 40)\n\n return H\n}\n\nmodule.exports = Sha384\n","var inherits = require('inherits')\nvar Hash = require('./hash')\nvar Buffer = require('safe-buffer').Buffer\n\nvar K = [\n 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd,\n 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc,\n 0x3956c25b, 0xf348b538, 0x59f111f1, 0xb605d019,\n 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118,\n 0xd807aa98, 0xa3030242, 0x12835b01, 0x45706fbe,\n 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2,\n 0x72be5d74, 0xf27b896f, 0x80deb1fe, 0x3b1696b1,\n 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694,\n 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3,\n 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65,\n 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483,\n 0x5cb0a9dc, 0xbd41fbd4, 0x76f988da, 0x831153b5,\n 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210,\n 0xb00327c8, 0x98fb213f, 0xbf597fc7, 0xbeef0ee4,\n 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725,\n 0x06ca6351, 0xe003826f, 0x14292967, 0x0a0e6e70,\n 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926,\n 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df,\n 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8,\n 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b,\n 0xa2bfe8a1, 0x4cf10364, 0xa81a664b, 0xbc423001,\n 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30,\n 0xd192e819, 0xd6ef5218, 0xd6990624, 0x5565a910,\n 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8,\n 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, 0x5141ab53,\n 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8,\n 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb,\n 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3,\n 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60,\n 0x84c87814, 0xa1f0ab72, 0x8cc70208, 0x1a6439ec,\n 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9,\n 0xbef9a3f7, 0xb2c67915, 0xc67178f2, 0xe372532b,\n 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207,\n 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, 0xee6ed178,\n 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6,\n 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b,\n 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493,\n 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c,\n 0x4cc5d4be, 0xcb3e42b6, 0x597f299c, 0xfc657e2a,\n 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817\n]\n\nvar W = new Array(160)\n\nfunction Sha512 () {\n this.init()\n this._w = W\n\n Hash.call(this, 128, 112)\n}\n\ninherits(Sha512, Hash)\n\nSha512.prototype.init = function () {\n this._ah = 0x6a09e667\n this._bh = 0xbb67ae85\n this._ch = 0x3c6ef372\n this._dh = 0xa54ff53a\n this._eh = 0x510e527f\n this._fh = 0x9b05688c\n this._gh = 0x1f83d9ab\n this._hh = 0x5be0cd19\n\n this._al = 0xf3bcc908\n this._bl = 0x84caa73b\n this._cl = 0xfe94f82b\n this._dl = 0x5f1d36f1\n this._el = 0xade682d1\n this._fl = 0x2b3e6c1f\n this._gl = 0xfb41bd6b\n this._hl = 0x137e2179\n\n return this\n}\n\nfunction Ch (x, y, z) {\n return z ^ (x & (y ^ z))\n}\n\nfunction maj (x, y, z) {\n return (x & y) | (z & (x | y))\n}\n\nfunction sigma0 (x, xl) {\n return (x >>> 28 | xl << 4) ^ (xl >>> 2 | x << 30) ^ (xl >>> 7 | x << 25)\n}\n\nfunction sigma1 (x, xl) {\n return (x >>> 14 | xl << 18) ^ (x >>> 18 | xl << 14) ^ (xl >>> 9 | x << 23)\n}\n\nfunction Gamma0 (x, xl) {\n return (x >>> 1 | xl << 31) ^ (x >>> 8 | xl << 24) ^ (x >>> 7)\n}\n\nfunction Gamma0l (x, xl) {\n return (x >>> 1 | xl << 31) ^ (x >>> 8 | xl << 24) ^ (x >>> 7 | xl << 25)\n}\n\nfunction Gamma1 (x, xl) {\n return (x >>> 19 | xl << 13) ^ (xl >>> 29 | x << 3) ^ (x >>> 6)\n}\n\nfunction Gamma1l (x, xl) {\n return (x >>> 19 | xl << 13) ^ (xl >>> 29 | x << 3) ^ (x >>> 6 | xl << 26)\n}\n\nfunction getCarry (a, b) {\n return (a >>> 0) < (b >>> 0) ? 1 : 0\n}\n\nSha512.prototype._update = function (M) {\n var W = this._w\n\n var ah = this._ah | 0\n var bh = this._bh | 0\n var ch = this._ch | 0\n var dh = this._dh | 0\n var eh = this._eh | 0\n var fh = this._fh | 0\n var gh = this._gh | 0\n var hh = this._hh | 0\n\n var al = this._al | 0\n var bl = this._bl | 0\n var cl = this._cl | 0\n var dl = this._dl | 0\n var el = this._el | 0\n var fl = this._fl | 0\n var gl = this._gl | 0\n var hl = this._hl | 0\n\n for (var i = 0; i < 32; i += 2) {\n W[i] = M.readInt32BE(i * 4)\n W[i + 1] = M.readInt32BE(i * 4 + 4)\n }\n for (; i < 160; i += 2) {\n var xh = W[i - 15 * 2]\n var xl = W[i - 15 * 2 + 1]\n var gamma0 = Gamma0(xh, xl)\n var gamma0l = Gamma0l(xl, xh)\n\n xh = W[i - 2 * 2]\n xl = W[i - 2 * 2 + 1]\n var gamma1 = Gamma1(xh, xl)\n var gamma1l = Gamma1l(xl, xh)\n\n // W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]\n var Wi7h = W[i - 7 * 2]\n var Wi7l = W[i - 7 * 2 + 1]\n\n var Wi16h = W[i - 16 * 2]\n var Wi16l = W[i - 16 * 2 + 1]\n\n var Wil = (gamma0l + Wi7l) | 0\n var Wih = (gamma0 + Wi7h + getCarry(Wil, gamma0l)) | 0\n Wil = (Wil + gamma1l) | 0\n Wih = (Wih + gamma1 + getCarry(Wil, gamma1l)) | 0\n Wil = (Wil + Wi16l) | 0\n Wih = (Wih + Wi16h + getCarry(Wil, Wi16l)) | 0\n\n W[i] = Wih\n W[i + 1] = Wil\n }\n\n for (var j = 0; j < 160; j += 2) {\n Wih = W[j]\n Wil = W[j + 1]\n\n var majh = maj(ah, bh, ch)\n var majl = maj(al, bl, cl)\n\n var sigma0h = sigma0(ah, al)\n var sigma0l = sigma0(al, ah)\n var sigma1h = sigma1(eh, el)\n var sigma1l = sigma1(el, eh)\n\n // t1 = h + sigma1 + ch + K[j] + W[j]\n var Kih = K[j]\n var Kil = K[j + 1]\n\n var chh = Ch(eh, fh, gh)\n var chl = Ch(el, fl, gl)\n\n var t1l = (hl + sigma1l) | 0\n var t1h = (hh + sigma1h + getCarry(t1l, hl)) | 0\n t1l = (t1l + chl) | 0\n t1h = (t1h + chh + getCarry(t1l, chl)) | 0\n t1l = (t1l + Kil) | 0\n t1h = (t1h + Kih + getCarry(t1l, Kil)) | 0\n t1l = (t1l + Wil) | 0\n t1h = (t1h + Wih + getCarry(t1l, Wil)) | 0\n\n // t2 = sigma0 + maj\n var t2l = (sigma0l + majl) | 0\n var t2h = (sigma0h + majh + getCarry(t2l, sigma0l)) | 0\n\n hh = gh\n hl = gl\n gh = fh\n gl = fl\n fh = eh\n fl = el\n el = (dl + t1l) | 0\n eh = (dh + t1h + getCarry(el, dl)) | 0\n dh = ch\n dl = cl\n ch = bh\n cl = bl\n bh = ah\n bl = al\n al = (t1l + t2l) | 0\n ah = (t1h + t2h + getCarry(al, t1l)) | 0\n }\n\n this._al = (this._al + al) | 0\n this._bl = (this._bl + bl) | 0\n this._cl = (this._cl + cl) | 0\n this._dl = (this._dl + dl) | 0\n this._el = (this._el + el) | 0\n this._fl = (this._fl + fl) | 0\n this._gl = (this._gl + gl) | 0\n this._hl = (this._hl + hl) | 0\n\n this._ah = (this._ah + ah + getCarry(this._al, al)) | 0\n this._bh = (this._bh + bh + getCarry(this._bl, bl)) | 0\n this._ch = (this._ch + ch + getCarry(this._cl, cl)) | 0\n this._dh = (this._dh + dh + getCarry(this._dl, dl)) | 0\n this._eh = (this._eh + eh + getCarry(this._el, el)) | 0\n this._fh = (this._fh + fh + getCarry(this._fl, fl)) | 0\n this._gh = (this._gh + gh + getCarry(this._gl, gl)) | 0\n this._hh = (this._hh + hh + getCarry(this._hl, hl)) | 0\n}\n\nSha512.prototype._hash = function () {\n var H = Buffer.allocUnsafe(64)\n\n function writeInt64BE (h, l, offset) {\n H.writeInt32BE(h, offset)\n H.writeInt32BE(l, offset + 4)\n }\n\n writeInt64BE(this._ah, this._al, 0)\n writeInt64BE(this._bh, this._bl, 8)\n writeInt64BE(this._ch, this._cl, 16)\n writeInt64BE(this._dh, this._dl, 24)\n writeInt64BE(this._eh, this._el, 32)\n writeInt64BE(this._fh, this._fl, 40)\n writeInt64BE(this._gh, this._gl, 48)\n writeInt64BE(this._hh, this._hl, 56)\n\n return H\n}\n\nmodule.exports = Sha512\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nmodule.exports = Stream;\n\nvar EE = require('events').EventEmitter;\nvar inherits = require('inherits');\n\ninherits(Stream, EE);\nStream.Readable = require('readable-stream/lib/_stream_readable.js');\nStream.Writable = require('readable-stream/lib/_stream_writable.js');\nStream.Duplex = require('readable-stream/lib/_stream_duplex.js');\nStream.Transform = require('readable-stream/lib/_stream_transform.js');\nStream.PassThrough = require('readable-stream/lib/_stream_passthrough.js');\nStream.finished = require('readable-stream/lib/internal/streams/end-of-stream.js')\nStream.pipeline = require('readable-stream/lib/internal/streams/pipeline.js')\n\n// Backwards-compat with node 0.4.x\nStream.Stream = Stream;\n\n\n\n// old-style streams. Note that the pipe method (the only relevant\n// part of this class) is overridden in the Readable class.\n\nfunction Stream() {\n EE.call(this);\n}\n\nStream.prototype.pipe = function(dest, options) {\n var source = this;\n\n function ondata(chunk) {\n if (dest.writable) {\n if (false === dest.write(chunk) && source.pause) {\n source.pause();\n }\n }\n }\n\n source.on('data', ondata);\n\n function ondrain() {\n if (source.readable && source.resume) {\n source.resume();\n }\n }\n\n dest.on('drain', ondrain);\n\n // If the 'end' option is not supplied, dest.end() will be called when\n // source gets the 'end' or 'close' events. Only dest.end() once.\n if (!dest._isStdio && (!options || options.end !== false)) {\n source.on('end', onend);\n source.on('close', onclose);\n }\n\n var didOnEnd = false;\n function onend() {\n if (didOnEnd) return;\n didOnEnd = true;\n\n dest.end();\n }\n\n\n function onclose() {\n if (didOnEnd) return;\n didOnEnd = true;\n\n if (typeof dest.destroy === 'function') dest.destroy();\n }\n\n // don't leave dangling pipes when there are errors.\n function onerror(er) {\n cleanup();\n if (EE.listenerCount(this, 'error') === 0) {\n throw er; // Unhandled stream error in pipe.\n }\n }\n\n source.on('error', onerror);\n dest.on('error', onerror);\n\n // remove all the event listeners that were added.\n function cleanup() {\n source.removeListener('data', ondata);\n dest.removeListener('drain', ondrain);\n\n source.removeListener('end', onend);\n source.removeListener('close', onclose);\n\n source.removeListener('error', onerror);\n dest.removeListener('error', onerror);\n\n source.removeListener('end', cleanup);\n source.removeListener('close', cleanup);\n\n dest.removeListener('close', cleanup);\n }\n\n source.on('end', cleanup);\n source.on('close', cleanup);\n\n dest.on('close', cleanup);\n\n dest.emit('pipe', source);\n\n // Allow for unix-like usage: A.pipe(B).pipe(C)\n return dest;\n};\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\n/**/\n\nvar Buffer = require('safe-buffer').Buffer;\n/**/\n\nvar isEncoding = Buffer.isEncoding || function (encoding) {\n encoding = '' + encoding;\n switch (encoding && encoding.toLowerCase()) {\n case 'hex':case 'utf8':case 'utf-8':case 'ascii':case 'binary':case 'base64':case 'ucs2':case 'ucs-2':case 'utf16le':case 'utf-16le':case 'raw':\n return true;\n default:\n return false;\n }\n};\n\nfunction _normalizeEncoding(enc) {\n if (!enc) return 'utf8';\n var retried;\n while (true) {\n switch (enc) {\n case 'utf8':\n case 'utf-8':\n return 'utf8';\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return 'utf16le';\n case 'latin1':\n case 'binary':\n return 'latin1';\n case 'base64':\n case 'ascii':\n case 'hex':\n return enc;\n default:\n if (retried) return; // undefined\n enc = ('' + enc).toLowerCase();\n retried = true;\n }\n }\n};\n\n// Do not cache `Buffer.isEncoding` when checking encoding names as some\n// modules monkey-patch it to support additional encodings\nfunction normalizeEncoding(enc) {\n var nenc = _normalizeEncoding(enc);\n if (typeof nenc !== 'string' && (Buffer.isEncoding === isEncoding || !isEncoding(enc))) throw new Error('Unknown encoding: ' + enc);\n return nenc || enc;\n}\n\n// StringDecoder provides an interface for efficiently splitting a series of\n// buffers into a series of JS strings without breaking apart multi-byte\n// characters.\nexports.StringDecoder = StringDecoder;\nfunction StringDecoder(encoding) {\n this.encoding = normalizeEncoding(encoding);\n var nb;\n switch (this.encoding) {\n case 'utf16le':\n this.text = utf16Text;\n this.end = utf16End;\n nb = 4;\n break;\n case 'utf8':\n this.fillLast = utf8FillLast;\n nb = 4;\n break;\n case 'base64':\n this.text = base64Text;\n this.end = base64End;\n nb = 3;\n break;\n default:\n this.write = simpleWrite;\n this.end = simpleEnd;\n return;\n }\n this.lastNeed = 0;\n this.lastTotal = 0;\n this.lastChar = Buffer.allocUnsafe(nb);\n}\n\nStringDecoder.prototype.write = function (buf) {\n if (buf.length === 0) return '';\n var r;\n var i;\n if (this.lastNeed) {\n r = this.fillLast(buf);\n if (r === undefined) return '';\n i = this.lastNeed;\n this.lastNeed = 0;\n } else {\n i = 0;\n }\n if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i);\n return r || '';\n};\n\nStringDecoder.prototype.end = utf8End;\n\n// Returns only complete characters in a Buffer\nStringDecoder.prototype.text = utf8Text;\n\n// Attempts to complete a partial non-UTF-8 character using bytes from a Buffer\nStringDecoder.prototype.fillLast = function (buf) {\n if (this.lastNeed <= buf.length) {\n buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed);\n return this.lastChar.toString(this.encoding, 0, this.lastTotal);\n }\n buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length);\n this.lastNeed -= buf.length;\n};\n\n// Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a\n// continuation byte. If an invalid byte is detected, -2 is returned.\nfunction utf8CheckByte(byte) {\n if (byte <= 0x7F) return 0;else if (byte >> 5 === 0x06) return 2;else if (byte >> 4 === 0x0E) return 3;else if (byte >> 3 === 0x1E) return 4;\n return byte >> 6 === 0x02 ? -1 : -2;\n}\n\n// Checks at most 3 bytes at the end of a Buffer in order to detect an\n// incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4)\n// needed to complete the UTF-8 character (if applicable) are returned.\nfunction utf8CheckIncomplete(self, buf, i) {\n var j = buf.length - 1;\n if (j < i) return 0;\n var nb = utf8CheckByte(buf[j]);\n if (nb >= 0) {\n if (nb > 0) self.lastNeed = nb - 1;\n return nb;\n }\n if (--j < i || nb === -2) return 0;\n nb = utf8CheckByte(buf[j]);\n if (nb >= 0) {\n if (nb > 0) self.lastNeed = nb - 2;\n return nb;\n }\n if (--j < i || nb === -2) return 0;\n nb = utf8CheckByte(buf[j]);\n if (nb >= 0) {\n if (nb > 0) {\n if (nb === 2) nb = 0;else self.lastNeed = nb - 3;\n }\n return nb;\n }\n return 0;\n}\n\n// Validates as many continuation bytes for a multi-byte UTF-8 character as\n// needed or are available. If we see a non-continuation byte where we expect\n// one, we \"replace\" the validated continuation bytes we've seen so far with\n// a single UTF-8 replacement character ('\\ufffd'), to match v8's UTF-8 decoding\n// behavior. The continuation byte check is included three times in the case\n// where all of the continuation bytes for a character exist in the same buffer.\n// It is also done this way as a slight performance increase instead of using a\n// loop.\nfunction utf8CheckExtraBytes(self, buf, p) {\n if ((buf[0] & 0xC0) !== 0x80) {\n self.lastNeed = 0;\n return '\\ufffd';\n }\n if (self.lastNeed > 1 && buf.length > 1) {\n if ((buf[1] & 0xC0) !== 0x80) {\n self.lastNeed = 1;\n return '\\ufffd';\n }\n if (self.lastNeed > 2 && buf.length > 2) {\n if ((buf[2] & 0xC0) !== 0x80) {\n self.lastNeed = 2;\n return '\\ufffd';\n }\n }\n }\n}\n\n// Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer.\nfunction utf8FillLast(buf) {\n var p = this.lastTotal - this.lastNeed;\n var r = utf8CheckExtraBytes(this, buf, p);\n if (r !== undefined) return r;\n if (this.lastNeed <= buf.length) {\n buf.copy(this.lastChar, p, 0, this.lastNeed);\n return this.lastChar.toString(this.encoding, 0, this.lastTotal);\n }\n buf.copy(this.lastChar, p, 0, buf.length);\n this.lastNeed -= buf.length;\n}\n\n// Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a\n// partial character, the character's bytes are buffered until the required\n// number of bytes are available.\nfunction utf8Text(buf, i) {\n var total = utf8CheckIncomplete(this, buf, i);\n if (!this.lastNeed) return buf.toString('utf8', i);\n this.lastTotal = total;\n var end = buf.length - (total - this.lastNeed);\n buf.copy(this.lastChar, 0, end);\n return buf.toString('utf8', i, end);\n}\n\n// For UTF-8, a replacement character is added when ending on a partial\n// character.\nfunction utf8End(buf) {\n var r = buf && buf.length ? this.write(buf) : '';\n if (this.lastNeed) return r + '\\ufffd';\n return r;\n}\n\n// UTF-16LE typically needs two bytes per character, but even if we have an even\n// number of bytes available, we need to check if we end on a leading/high\n// surrogate. In that case, we need to wait for the next two bytes in order to\n// decode the last character properly.\nfunction utf16Text(buf, i) {\n if ((buf.length - i) % 2 === 0) {\n var r = buf.toString('utf16le', i);\n if (r) {\n var c = r.charCodeAt(r.length - 1);\n if (c >= 0xD800 && c <= 0xDBFF) {\n this.lastNeed = 2;\n this.lastTotal = 4;\n this.lastChar[0] = buf[buf.length - 2];\n this.lastChar[1] = buf[buf.length - 1];\n return r.slice(0, -1);\n }\n }\n return r;\n }\n this.lastNeed = 1;\n this.lastTotal = 2;\n this.lastChar[0] = buf[buf.length - 1];\n return buf.toString('utf16le', i, buf.length - 1);\n}\n\n// For UTF-16LE we do not explicitly append special replacement characters if we\n// end on a partial character, we simply let v8 handle that.\nfunction utf16End(buf) {\n var r = buf && buf.length ? this.write(buf) : '';\n if (this.lastNeed) {\n var end = this.lastTotal - this.lastNeed;\n return r + this.lastChar.toString('utf16le', 0, end);\n }\n return r;\n}\n\nfunction base64Text(buf, i) {\n var n = (buf.length - i) % 3;\n if (n === 0) return buf.toString('base64', i);\n this.lastNeed = 3 - n;\n this.lastTotal = 3;\n if (n === 1) {\n this.lastChar[0] = buf[buf.length - 1];\n } else {\n this.lastChar[0] = buf[buf.length - 2];\n this.lastChar[1] = buf[buf.length - 1];\n }\n return buf.toString('base64', i, buf.length - n);\n}\n\nfunction base64End(buf) {\n var r = buf && buf.length ? this.write(buf) : '';\n if (this.lastNeed) return r + this.lastChar.toString('base64', 0, 3 - this.lastNeed);\n return r;\n}\n\n// Pass bytes on through for single-byte encodings (e.g. ascii, latin1, hex)\nfunction simpleWrite(buf) {\n return buf.toString(this.encoding);\n}\n\nfunction simpleEnd(buf) {\n return buf && buf.length ? this.write(buf) : '';\n}","/*! safe-buffer. MIT License. Feross Aboukhadijeh */\n/* eslint-disable node/no-deprecated-api */\nvar buffer = require('buffer')\nvar Buffer = buffer.Buffer\n\n// alternative to using Object.keys for old browsers\nfunction copyProps (src, dst) {\n for (var key in src) {\n dst[key] = src[key]\n }\n}\nif (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) {\n module.exports = buffer\n} else {\n // Copy properties from require('buffer')\n copyProps(buffer, exports)\n exports.Buffer = SafeBuffer\n}\n\nfunction SafeBuffer (arg, encodingOrOffset, length) {\n return Buffer(arg, encodingOrOffset, length)\n}\n\nSafeBuffer.prototype = Object.create(Buffer.prototype)\n\n// Copy static methods from Buffer\ncopyProps(Buffer, SafeBuffer)\n\nSafeBuffer.from = function (arg, encodingOrOffset, length) {\n if (typeof arg === 'number') {\n throw new TypeError('Argument must not be a number')\n }\n return Buffer(arg, encodingOrOffset, length)\n}\n\nSafeBuffer.alloc = function (size, fill, encoding) {\n if (typeof size !== 'number') {\n throw new TypeError('Argument must be a number')\n }\n var buf = Buffer(size)\n if (fill !== undefined) {\n if (typeof encoding === 'string') {\n buf.fill(fill, encoding)\n } else {\n buf.fill(fill)\n }\n } else {\n buf.fill(0)\n }\n return buf\n}\n\nSafeBuffer.allocUnsafe = function (size) {\n if (typeof size !== 'number') {\n throw new TypeError('Argument must be a number')\n }\n return Buffer(size)\n}\n\nSafeBuffer.allocUnsafeSlow = function (size) {\n if (typeof size !== 'number') {\n throw new TypeError('Argument must be a number')\n }\n return buffer.SlowBuffer(size)\n}\n","\n/**\n * Module exports.\n */\n\nmodule.exports = deprecate;\n\n/**\n * Mark that a method should not be used.\n * Returns a modified function which warns once by default.\n *\n * If `localStorage.noDeprecation = true` is set, then it is a no-op.\n *\n * If `localStorage.throwDeprecation = true` is set, then deprecated functions\n * will throw an Error when invoked.\n *\n * If `localStorage.traceDeprecation = true` is set, then deprecated functions\n * will invoke `console.trace()` instead of `console.error()`.\n *\n * @param {Function} fn - the function to deprecate\n * @param {String} msg - the string to print to the console when `fn` is invoked\n * @returns {Function} a new \"deprecated\" version of `fn`\n * @api public\n */\n\nfunction deprecate (fn, msg) {\n if (config('noDeprecation')) {\n return fn;\n }\n\n var warned = false;\n function deprecated() {\n if (!warned) {\n if (config('throwDeprecation')) {\n throw new Error(msg);\n } else if (config('traceDeprecation')) {\n console.trace(msg);\n } else {\n console.warn(msg);\n }\n warned = true;\n }\n return fn.apply(this, arguments);\n }\n\n return deprecated;\n}\n\n/**\n * Checks `localStorage` for boolean values for the given `name`.\n *\n * @param {String} name\n * @returns {Boolean}\n * @api private\n */\n\nfunction config (name) {\n // accessing global.localStorage can trigger a DOMException in sandboxed iframes\n try {\n if (!global.localStorage) return false;\n } catch (_) {\n return false;\n }\n var val = global.localStorage[name];\n if (null == val) return false;\n return String(val).toLowerCase() === 'true';\n}\n","\nvar XML_CHARACTER_MAP = {\n '&': '&',\n '\"': '"',\n \"'\": ''',\n '<': '<',\n '>': '>'\n};\n\nfunction escapeForXML(string) {\n return string && string.replace\n ? string.replace(/([&\"<>'])/g, function(str, item) {\n return XML_CHARACTER_MAP[item];\n })\n : string;\n}\n\nmodule.exports = escapeForXML;\n","var escapeForXML = require('./escapeForXML');\nvar Stream = require('stream').Stream;\n\nvar DEFAULT_INDENT = ' ';\n\nfunction xml(input, options) {\n\n if (typeof options !== 'object') {\n options = {\n indent: options\n };\n }\n\n var stream = options.stream ? new Stream() : null,\n output = \"\",\n interrupted = false,\n indent = !options.indent ? ''\n : options.indent === true ? DEFAULT_INDENT\n : options.indent,\n instant = true;\n\n\n function delay (func) {\n if (!instant) {\n func();\n } else {\n process.nextTick(func);\n }\n }\n\n function append (interrupt, out) {\n if (out !== undefined) {\n output += out;\n }\n if (interrupt && !interrupted) {\n stream = stream || new Stream();\n interrupted = true;\n }\n if (interrupt && interrupted) {\n var data = output;\n delay(function () { stream.emit('data', data) });\n output = \"\";\n }\n }\n\n function add (value, last) {\n format(append, resolve(value, indent, indent ? 1 : 0), last);\n }\n\n function end() {\n if (stream) {\n var data = output;\n delay(function () {\n stream.emit('data', data);\n stream.emit('end');\n stream.readable = false;\n stream.emit('close');\n });\n }\n }\n\n function addXmlDeclaration(declaration) {\n var encoding = declaration.encoding || 'UTF-8',\n attr = { version: '1.0', encoding: encoding };\n\n if (declaration.standalone) {\n attr.standalone = declaration.standalone\n }\n\n add({'?xml': { _attr: attr } });\n output = output.replace('/>', '?>');\n }\n\n // disable delay delayed\n delay(function () { instant = false });\n\n if (options.declaration) {\n addXmlDeclaration(options.declaration);\n }\n\n if (input && input.forEach) {\n input.forEach(function (value, i) {\n var last;\n if (i + 1 === input.length)\n last = end;\n add(value, last);\n });\n } else {\n add(input, end);\n }\n\n if (stream) {\n stream.readable = true;\n return stream;\n }\n return output;\n}\n\nfunction element (/*input, …*/) {\n var input = Array.prototype.slice.call(arguments),\n self = {\n _elem: resolve(input)\n };\n\n self.push = function (input) {\n if (!this.append) {\n throw new Error(\"not assigned to a parent!\");\n }\n var that = this;\n var indent = this._elem.indent;\n format(this.append, resolve(\n input, indent, this._elem.icount + (indent ? 1 : 0)),\n function () { that.append(true) });\n };\n\n self.close = function (input) {\n if (input !== undefined) {\n this.push(input);\n }\n if (this.end) {\n this.end();\n }\n };\n\n return self;\n}\n\nfunction create_indent(character, count) {\n return (new Array(count || 0).join(character || ''))\n}\n\nfunction resolve(data, indent, indent_count) {\n indent_count = indent_count || 0;\n var indent_spaces = create_indent(indent, indent_count);\n var name;\n var values = data;\n var interrupt = false;\n\n if (typeof data === 'object') {\n var keys = Object.keys(data);\n name = keys[0];\n values = data[name];\n\n if (values && values._elem) {\n values._elem.name = name;\n values._elem.icount = indent_count;\n values._elem.indent = indent;\n values._elem.indents = indent_spaces;\n values._elem.interrupt = values;\n return values._elem;\n }\n }\n\n var attributes = [],\n content = [];\n\n var isStringContent;\n\n function get_attributes(obj){\n var keys = Object.keys(obj);\n keys.forEach(function(key){\n attributes.push(attribute(key, obj[key]));\n });\n }\n\n switch(typeof values) {\n case 'object':\n if (values === null) break;\n\n if (values._attr) {\n get_attributes(values._attr);\n }\n\n if (values._cdata) {\n content.push(\n ('/g, ']]]]>') + ']]>'\n );\n }\n\n if (values.forEach) {\n isStringContent = false;\n content.push('');\n values.forEach(function(value) {\n if (typeof value == 'object') {\n var _name = Object.keys(value)[0];\n\n if (_name == '_attr') {\n get_attributes(value._attr);\n } else {\n content.push(resolve(\n value, indent, indent_count + 1));\n }\n } else {\n //string\n content.pop();\n isStringContent=true;\n content.push(escapeForXML(value));\n }\n\n });\n if (!isStringContent) {\n content.push('');\n }\n }\n break;\n\n default:\n //string\n content.push(escapeForXML(values));\n\n }\n\n return {\n name: name,\n interrupt: interrupt,\n attributes: attributes,\n content: content,\n icount: indent_count,\n indents: indent_spaces,\n indent: indent\n };\n}\n\nfunction format(append, elem, end) {\n\n if (typeof elem != 'object') {\n return append(false, elem);\n }\n\n var len = elem.interrupt ? 1 : elem.content.length;\n\n function proceed () {\n while (elem.content.length) {\n var value = elem.content.shift();\n\n if (value === undefined) continue;\n if (interrupt(value)) return;\n\n format(append, value);\n }\n\n append(false, (len > 1 ? elem.indents : '')\n + (elem.name ? '' : '')\n + (elem.indent && !end ? '\\n' : ''));\n\n if (end) {\n end();\n }\n }\n\n function interrupt(value) {\n if (value.interrupt) {\n value.interrupt.append = append;\n value.interrupt.end = proceed;\n value.interrupt = false;\n append(true);\n return true;\n }\n return false;\n }\n\n append(false, elem.indents\n + (elem.name ? '<' + elem.name : '')\n + (elem.attributes.length ? ' ' + elem.attributes.join(' ') : '')\n + (len ? (elem.name ? '>' : '') : (elem.name ? '/>' : ''))\n + (elem.indent && len > 1 ? '\\n' : ''));\n\n if (!len) {\n return append(false, elem.indent ? '\\n' : '');\n }\n\n if (!interrupt(elem)) {\n proceed();\n }\n}\n\nfunction attribute(key, value) {\n return key + '=' + '\"' + escapeForXML(value) + '\"';\n}\n\nmodule.exports = xml;\nmodule.exports.element = module.exports.Element = element;\n","var map = {\n\t\"./all.js\": 5308,\n\t\"./auth/actions.js\": 5812,\n\t\"./auth/index.js\": 3705,\n\t\"./auth/reducers.js\": 3962,\n\t\"./auth/selectors.js\": 35,\n\t\"./auth/spec-wrap-actions.js\": 8302,\n\t\"./configs/actions.js\": 714,\n\t\"./configs/helpers.js\": 2256,\n\t\"./configs/index.js\": 1661,\n\t\"./configs/reducers.js\": 7743,\n\t\"./configs/selectors.js\": 9018,\n\t\"./configs/spec-actions.js\": 2698,\n\t\"./deep-linking/helpers.js\": 1970,\n\t\"./deep-linking/index.js\": 4980,\n\t\"./deep-linking/layout.js\": 5858,\n\t\"./deep-linking/operation-tag-wrapper.jsx\": 4584,\n\t\"./deep-linking/operation-wrapper.jsx\": 877,\n\t\"./download-url.js\": 8011,\n\t\"./err/actions.js\": 4966,\n\t\"./err/error-transformers/hook.js\": 6808,\n\t\"./err/error-transformers/transformers/not-of-type.js\": 2392,\n\t\"./err/error-transformers/transformers/parameter-oneof.js\": 1835,\n\t\"./err/index.js\": 7793,\n\t\"./err/reducers.js\": 3527,\n\t\"./err/selectors.js\": 7667,\n\t\"./filter/index.js\": 9978,\n\t\"./filter/opsFilter.js\": 4309,\n\t\"./layout/actions.js\": 5474,\n\t\"./layout/index.js\": 6821,\n\t\"./layout/reducers.js\": 5672,\n\t\"./layout/selectors.js\": 4400,\n\t\"./layout/spec-extensions/wrap-selector.js\": 8989,\n\t\"./logs/index.js\": 9150,\n\t\"./oas3/actions.js\": 7002,\n\t\"./oas3/auth-extensions/wrap-selectors.js\": 3723,\n\t\"./oas3/components/callbacks.jsx\": 3427,\n\t\"./oas3/components/http-auth.jsx\": 6775,\n\t\"./oas3/components/index.js\": 6467,\n\t\"./oas3/components/operation-link.jsx\": 5757,\n\t\"./oas3/components/operation-servers.jsx\": 6796,\n\t\"./oas3/components/request-body-editor.jsx\": 5327,\n\t\"./oas3/components/request-body.jsx\": 2458,\n\t\"./oas3/components/servers-container.jsx\": 9928,\n\t\"./oas3/components/servers.jsx\": 6617,\n\t\"./oas3/helpers.jsx\": 7779,\n\t\"./oas3/index.js\": 7451,\n\t\"./oas3/reducers.js\": 9666,\n\t\"./oas3/selectors.js\": 5065,\n\t\"./oas3/spec-extensions/selectors.js\": 1741,\n\t\"./oas3/spec-extensions/wrap-selectors.js\": 2044,\n\t\"./oas3/wrap-components/auth-item.jsx\": 356,\n\t\"./oas3/wrap-components/index.js\": 7761,\n\t\"./oas3/wrap-components/json-schema-string.jsx\": 287,\n\t\"./oas3/wrap-components/markdown.jsx\": 2460,\n\t\"./oas3/wrap-components/model.jsx\": 3499,\n\t\"./oas3/wrap-components/online-validator-badge.js\": 58,\n\t\"./oas3/wrap-components/version-stamp.jsx\": 9487,\n\t\"./on-complete/index.js\": 8560,\n\t\"./request-snippets/fn.js\": 4624,\n\t\"./request-snippets/index.js\": 6575,\n\t\"./request-snippets/request-snippets.jsx\": 4206,\n\t\"./request-snippets/selectors.js\": 4669,\n\t\"./safe-render/components/error-boundary.jsx\": 6195,\n\t\"./safe-render/components/fallback.jsx\": 9403,\n\t\"./safe-render/fn.jsx\": 6189,\n\t\"./safe-render/index.js\": 8102,\n\t\"./samples/fn.js\": 2473,\n\t\"./samples/index.js\": 8883,\n\t\"./spec/actions.js\": 1737,\n\t\"./spec/index.js\": 7038,\n\t\"./spec/reducers.js\": 32,\n\t\"./spec/selectors.js\": 3881,\n\t\"./spec/wrap-actions.js\": 7508,\n\t\"./swagger-js/configs-wrap-actions.js\": 4852,\n\t\"./swagger-js/index.js\": 2990,\n\t\"./util/index.js\": 8525,\n\t\"./view/fn.js\": 8347,\n\t\"./view/index.js\": 3420,\n\t\"./view/root-injects.jsx\": 5005,\n\t\"core/plugins/all.js\": 5308,\n\t\"core/plugins/auth/actions.js\": 5812,\n\t\"core/plugins/auth/index.js\": 3705,\n\t\"core/plugins/auth/reducers.js\": 3962,\n\t\"core/plugins/auth/selectors.js\": 35,\n\t\"core/plugins/auth/spec-wrap-actions.js\": 8302,\n\t\"core/plugins/configs/actions.js\": 714,\n\t\"core/plugins/configs/helpers.js\": 2256,\n\t\"core/plugins/configs/index.js\": 1661,\n\t\"core/plugins/configs/reducers.js\": 7743,\n\t\"core/plugins/configs/selectors.js\": 9018,\n\t\"core/plugins/configs/spec-actions.js\": 2698,\n\t\"core/plugins/deep-linking/helpers.js\": 1970,\n\t\"core/plugins/deep-linking/index.js\": 4980,\n\t\"core/plugins/deep-linking/layout.js\": 5858,\n\t\"core/plugins/deep-linking/operation-tag-wrapper.jsx\": 4584,\n\t\"core/plugins/deep-linking/operation-wrapper.jsx\": 877,\n\t\"core/plugins/download-url.js\": 8011,\n\t\"core/plugins/err/actions.js\": 4966,\n\t\"core/plugins/err/error-transformers/hook.js\": 6808,\n\t\"core/plugins/err/error-transformers/transformers/not-of-type.js\": 2392,\n\t\"core/plugins/err/error-transformers/transformers/parameter-oneof.js\": 1835,\n\t\"core/plugins/err/index.js\": 7793,\n\t\"core/plugins/err/reducers.js\": 3527,\n\t\"core/plugins/err/selectors.js\": 7667,\n\t\"core/plugins/filter/index.js\": 9978,\n\t\"core/plugins/filter/opsFilter.js\": 4309,\n\t\"core/plugins/layout/actions.js\": 5474,\n\t\"core/plugins/layout/index.js\": 6821,\n\t\"core/plugins/layout/reducers.js\": 5672,\n\t\"core/plugins/layout/selectors.js\": 4400,\n\t\"core/plugins/layout/spec-extensions/wrap-selector.js\": 8989,\n\t\"core/plugins/logs/index.js\": 9150,\n\t\"core/plugins/oas3/actions.js\": 7002,\n\t\"core/plugins/oas3/auth-extensions/wrap-selectors.js\": 3723,\n\t\"core/plugins/oas3/components/callbacks.jsx\": 3427,\n\t\"core/plugins/oas3/components/http-auth.jsx\": 6775,\n\t\"core/plugins/oas3/components/index.js\": 6467,\n\t\"core/plugins/oas3/components/operation-link.jsx\": 5757,\n\t\"core/plugins/oas3/components/operation-servers.jsx\": 6796,\n\t\"core/plugins/oas3/components/request-body-editor.jsx\": 5327,\n\t\"core/plugins/oas3/components/request-body.jsx\": 2458,\n\t\"core/plugins/oas3/components/servers-container.jsx\": 9928,\n\t\"core/plugins/oas3/components/servers.jsx\": 6617,\n\t\"core/plugins/oas3/helpers.jsx\": 7779,\n\t\"core/plugins/oas3/index.js\": 7451,\n\t\"core/plugins/oas3/reducers.js\": 9666,\n\t\"core/plugins/oas3/selectors.js\": 5065,\n\t\"core/plugins/oas3/spec-extensions/selectors.js\": 1741,\n\t\"core/plugins/oas3/spec-extensions/wrap-selectors.js\": 2044,\n\t\"core/plugins/oas3/wrap-components/auth-item.jsx\": 356,\n\t\"core/plugins/oas3/wrap-components/index.js\": 7761,\n\t\"core/plugins/oas3/wrap-components/json-schema-string.jsx\": 287,\n\t\"core/plugins/oas3/wrap-components/markdown.jsx\": 2460,\n\t\"core/plugins/oas3/wrap-components/model.jsx\": 3499,\n\t\"core/plugins/oas3/wrap-components/online-validator-badge.js\": 58,\n\t\"core/plugins/oas3/wrap-components/version-stamp.jsx\": 9487,\n\t\"core/plugins/on-complete/index.js\": 8560,\n\t\"core/plugins/request-snippets/fn.js\": 4624,\n\t\"core/plugins/request-snippets/index.js\": 6575,\n\t\"core/plugins/request-snippets/request-snippets.jsx\": 4206,\n\t\"core/plugins/request-snippets/selectors.js\": 4669,\n\t\"core/plugins/safe-render/components/error-boundary.jsx\": 6195,\n\t\"core/plugins/safe-render/components/fallback.jsx\": 9403,\n\t\"core/plugins/safe-render/fn.jsx\": 6189,\n\t\"core/plugins/safe-render/index.js\": 8102,\n\t\"core/plugins/samples/fn.js\": 2473,\n\t\"core/plugins/samples/index.js\": 8883,\n\t\"core/plugins/spec/actions.js\": 1737,\n\t\"core/plugins/spec/index.js\": 7038,\n\t\"core/plugins/spec/reducers.js\": 32,\n\t\"core/plugins/spec/selectors.js\": 3881,\n\t\"core/plugins/spec/wrap-actions.js\": 7508,\n\t\"core/plugins/swagger-js/configs-wrap-actions.js\": 4852,\n\t\"core/plugins/swagger-js/index.js\": 2990,\n\t\"core/plugins/util/index.js\": 8525,\n\t\"core/plugins/view/fn.js\": 8347,\n\t\"core/plugins/view/index.js\": 3420,\n\t\"core/plugins/view/root-injects.jsx\": 5005\n};\n\n\nfunction webpackContext(req) {\n\tvar id = webpackContextResolve(req);\n\treturn __webpack_require__(id);\n}\nfunction webpackContextResolve(req) {\n\tif(!__webpack_require__.o(map, req)) {\n\t\tvar e = new Error(\"Cannot find module '\" + req + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\treturn map[req];\n}\nwebpackContext.keys = function webpackContextKeys() {\n\treturn Object.keys(map);\n};\nwebpackContext.resolve = webpackContextResolve;\nmodule.exports = webpackContext;\nwebpackContext.id = 5102;","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_array_from_6be643d1__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_array_is_array_6a843f38__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_bind_23a689fe__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_concat_ad403b1a__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_entries_97fed13d__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_every_ac7bb0bc__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_filter_13f270a8__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_find_0ad1164d__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_for_each_f55cb86b__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_includes_c33ad5d5__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_index_of_5fb826c6__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_keys_3b8fec80__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_map_868765ae__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_reduce_e87b61a7__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_slice_9832b507__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_some_50ff1b2d__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_sort_abe23e03__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_starts_with_a4b73998__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_trim_ca5b709e__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_json_stringify_1bf7a515__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_map_16a511c8__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_object_assign_e13b6141__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_object_keys_e09d3035__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_object_values_550c3b22__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_set_timeout_d31e8027__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_url_4cfab046__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_assertThisInitialized_d1aa492f__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_classCallCheck_bc61e5c4__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_createClass_e5c14ae7__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_createForOfIteratorHelper_d12c79e6__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_createSuper_c6ffd7da__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_defineProperty_807a2698__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_extends_d20d3ceb__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_inherits_12dd5677__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_objectSpread2_89fd8f56__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_objectWithoutProperties_9beedba5__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_slicedToArray_5206d485__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_toConsumableArray_8b04f6fd__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_helpers_typeof_f63a1f14__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = __WEBPACK_EXTERNAL_MODULE_base64_js_f145eb6e__;","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_classnames__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = __WEBPACK_EXTERNAL_MODULE_ieee754__;","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"List\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__.List, [\"Map\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__.Map, [\"OrderedMap\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__.OrderedMap, [\"Seq\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__.Seq, [\"Set\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__.Set, [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__[\"default\"], [\"fromJS\"]: () => __WEBPACK_EXTERNAL_MODULE_immutable__.fromJS });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"JSON_SCHEMA\"]: () => __WEBPACK_EXTERNAL_MODULE_js_yaml_78384032__.JSON_SCHEMA, [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_js_yaml_78384032__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_get_9427f899__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_isFunction_f90b20d6__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_memoize_2b5bc477__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_prop_types_adfe8e31__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"Component\"]: () => __WEBPACK_EXTERNAL_MODULE_react__.Component, [\"PureComponent\"]: () => __WEBPACK_EXTERNAL_MODULE_react__.PureComponent, [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react__[\"default\"], [\"useEffect\"]: () => __WEBPACK_EXTERNAL_MODULE_react__.useEffect, [\"useRef\"]: () => __WEBPACK_EXTERNAL_MODULE_react__.useRef, [\"useState\"]: () => __WEBPACK_EXTERNAL_MODULE_react__.useState });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"CopyToClipboard\"]: () => __WEBPACK_EXTERNAL_MODULE_react_copy_to_clipboard_5b11dd57__.CopyToClipboard });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_immutable_proptypes_89c7d083__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"applyMiddleware\"]: () => __WEBPACK_EXTERNAL_MODULE_redux__.applyMiddleware, [\"bindActionCreators\"]: () => __WEBPACK_EXTERNAL_MODULE_redux__.bindActionCreators, [\"compose\"]: () => __WEBPACK_EXTERNAL_MODULE_redux__.compose, [\"createStore\"]: () => __WEBPACK_EXTERNAL_MODULE_redux__.createStore });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"Remarkable\"]: () => __WEBPACK_EXTERNAL_MODULE_remarkable__.Remarkable });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"createSelector\"]: () => __WEBPACK_EXTERNAL_MODULE_reselect__.createSelector });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"serializeError\"]: () => __WEBPACK_EXTERNAL_MODULE_serialize_error_5f2df3e5__.serializeError });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"opId\"]: () => __WEBPACK_EXTERNAL_MODULE_swagger_client_es_helpers_4d7bea47__.opId });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nmodule.exports = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_url_parse_6456105f__[\"default\"] });","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_last_index_of_bbdfc000__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"combineReducers\"]: () => __WEBPACK_EXTERNAL_MODULE_redux_immutable_446c9f82__.combineReducers });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_merge_cf99375a__[\"default\"] });","import React from \"react\"\nimport { createStore, applyMiddleware, bindActionCreators, compose } from \"redux\"\nimport Im, { fromJS, Map } from \"immutable\"\nimport deepExtend from \"deep-extend\"\nimport { combineReducers } from \"redux-immutable\"\nimport { serializeError } from \"serialize-error\"\nimport merge from \"lodash/merge\"\nimport { NEW_THROWN_ERR } from \"corePlugins/err/actions\"\nimport win from \"core/window\"\n\nimport { systemThunkMiddleware, isFn, objMap, objReduce, isObject, isArray, isFunc } from \"core/utils\"\n\nconst idFn = a => a\n\n// Apply middleware that gets sandwitched between `dispatch` and the reducer function(s)\nfunction createStoreWithMiddleware(rootReducer, initialState, getSystem) {\n\n let middlwares = [\n // createLogger( {\n // stateTransformer: state => state && state.toJS()\n // } ),\n systemThunkMiddleware( getSystem )\n ]\n\n const composeEnhancers = win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose\n\n return createStore(rootReducer, initialState, composeEnhancers(\n applyMiddleware( ...middlwares )\n ))\n}\n\nexport default class Store {\n\n constructor(opts={}) {\n deepExtend(this, {\n state: {},\n plugins: [],\n pluginsOptions: {},\n system: {\n configs: {},\n fn: {},\n components: {},\n rootInjects: {},\n statePlugins: {}\n },\n boundSystem: {},\n toolbox: {}\n }, opts)\n\n this.getSystem = this._getSystem.bind(this)\n\n // Bare system (nothing in it, besides the state)\n this.store = configureStore(idFn, fromJS(this.state), this.getSystem )\n\n // will be the system + Im, we can add more tools when we need to\n this.buildSystem(false)\n\n // Bootstrap plugins\n this.register(this.plugins)\n }\n\n getStore() {\n return this.store\n }\n\n register(plugins, rebuild=true) {\n var pluginSystem = combinePlugins(plugins, this.getSystem(), this.pluginsOptions)\n systemExtend(this.system, pluginSystem)\n if(rebuild) {\n this.buildSystem()\n }\n\n const needAnotherRebuild = callAfterLoad.call(this.system, plugins, this.getSystem())\n\n if(needAnotherRebuild) {\n this.buildSystem()\n }\n }\n\n buildSystem(buildReducer=true) {\n let dispatch = this.getStore().dispatch\n let getState = this.getStore().getState\n\n this.boundSystem = Object.assign({},\n this.getRootInjects(),\n this.getWrappedAndBoundActions(dispatch),\n this.getWrappedAndBoundSelectors(getState, this.getSystem),\n this.getStateThunks(getState),\n this.getFn(),\n this.getConfigs()\n )\n\n if(buildReducer)\n this.rebuildReducer()\n }\n\n _getSystem() {\n return this.boundSystem\n }\n\n getRootInjects() {\n return Object.assign({\n getSystem: this.getSystem,\n getStore: this.getStore.bind(this),\n getComponents: this.getComponents.bind(this),\n getState: this.getStore().getState,\n getConfigs: this._getConfigs.bind(this),\n Im,\n React\n }, this.system.rootInjects || {})\n }\n\n _getConfigs(){\n return this.system.configs\n }\n\n getConfigs() {\n return {\n configs: this.system.configs\n }\n }\n\n setConfigs(configs) {\n this.system.configs = configs\n }\n\n rebuildReducer() {\n this.store.replaceReducer(buildReducer(this.system.statePlugins))\n }\n\n /**\n * Generic getter from system.statePlugins\n *\n */\n getType(name) {\n let upName = name[0].toUpperCase() + name.slice(1)\n return objReduce(this.system.statePlugins, (val, namespace) => {\n let thing = val[name]\n if(thing)\n return {[namespace+upName]: thing}\n })\n }\n\n getSelectors() {\n return this.getType(\"selectors\")\n }\n\n getActions() {\n let actionHolders = this.getType(\"actions\")\n\n return objMap(actionHolders, (actions) => {\n return objReduce(actions, (action, actionName) => {\n if(isFn(action))\n return {[actionName]: action}\n })\n })\n }\n\n getWrappedAndBoundActions(dispatch) {\n let actionGroups = this.getBoundActions(dispatch)\n return objMap(actionGroups, (actions, actionGroupName) => {\n let wrappers = this.system.statePlugins[actionGroupName.slice(0,-7)].wrapActions\n if(wrappers) {\n return objMap(actions, (action, actionName) => {\n let wrap = wrappers[actionName]\n if(!wrap) {\n return action\n }\n\n if(!Array.isArray(wrap)) {\n wrap = [wrap]\n }\n return wrap.reduce((acc, fn) => {\n let newAction = (...args) => {\n return fn(acc, this.getSystem())(...args)\n }\n if(!isFn(newAction)) {\n throw new TypeError(\"wrapActions needs to return a function that returns a new function (ie the wrapped action)\")\n }\n return wrapWithTryCatch(newAction)\n }, action || Function.prototype)\n })\n }\n return actions\n })\n }\n\n getWrappedAndBoundSelectors(getState, getSystem) {\n let selectorGroups = this.getBoundSelectors(getState, getSystem)\n return objMap(selectorGroups, (selectors, selectorGroupName) => {\n let stateName = [selectorGroupName.slice(0, -9)] // selectors = 9 chars\n let wrappers = this.system.statePlugins[stateName].wrapSelectors\n if(wrappers) {\n return objMap(selectors, (selector, selectorName) => {\n let wrap = wrappers[selectorName]\n if(!wrap) {\n return selector\n }\n\n if(!Array.isArray(wrap)) {\n wrap = [wrap]\n }\n return wrap.reduce((acc, fn) => {\n let wrappedSelector = (...args) => {\n return fn(acc, this.getSystem())(getState().getIn(stateName), ...args)\n }\n if(!isFn(wrappedSelector)) {\n throw new TypeError(\"wrapSelector needs to return a function that returns a new function (ie the wrapped action)\")\n }\n return wrappedSelector\n }, selector || Function.prototype)\n })\n }\n return selectors\n })\n }\n\n getStates(state) {\n return Object.keys(this.system.statePlugins).reduce((obj, key) => {\n obj[key] = state.get(key)\n return obj\n }, {})\n }\n\n getStateThunks(getState) {\n return Object.keys(this.system.statePlugins).reduce((obj, key) => {\n obj[key] = ()=> getState().get(key)\n return obj\n }, {})\n }\n\n getFn() {\n return {\n fn: this.system.fn\n }\n }\n\n getComponents(component) {\n const res = this.system.components[component]\n\n if(Array.isArray(res)) {\n return res.reduce((ori, wrapper) => {\n return wrapper(ori, this.getSystem())\n })\n }\n if(typeof component !== \"undefined\") {\n return this.system.components[component]\n }\n\n return this.system.components\n }\n\n getBoundSelectors(getState, getSystem) {\n return objMap(this.getSelectors(), (obj, key) => {\n let stateName = [key.slice(0, -9)] // selectors = 9 chars\n const getNestedState = ()=> getState().getIn(stateName)\n\n return objMap(obj, (fn) => {\n return (...args) => {\n let res = wrapWithTryCatch(fn).apply(null, [getNestedState(), ...args])\n\n // If a selector returns a function, give it the system - for advanced usage\n if(typeof(res) === \"function\")\n res = wrapWithTryCatch(res)(getSystem())\n\n return res\n }\n })\n })\n }\n\n getBoundActions(dispatch) {\n\n dispatch = dispatch || this.getStore().dispatch\n\n const actions = this.getActions()\n\n const process = creator =>{\n if( typeof( creator ) !== \"function\" ) {\n return objMap(creator, prop => process(prop))\n }\n\n return ( ...args )=>{\n var action = null\n try{\n action = creator( ...args )\n }\n catch( e ){\n action = {type: NEW_THROWN_ERR, error: true, payload: serializeError(e) }\n }\n finally{\n return action // eslint-disable-line no-unsafe-finally\n }\n }\n\n }\n return objMap(actions, actionCreator => bindActionCreators( process( actionCreator ), dispatch ) )\n }\n\n getMapStateToProps() {\n return () => {\n return Object.assign({}, this.getSystem())\n }\n }\n\n getMapDispatchToProps(extras) {\n return (dispatch) => {\n return deepExtend({}, this.getWrappedAndBoundActions(dispatch), this.getFn(), extras)\n }\n }\n\n}\n\nfunction combinePlugins(plugins, toolbox, pluginOptions) {\n if(isObject(plugins) && !isArray(plugins)) {\n return merge({}, plugins)\n }\n\n if(isFunc(plugins)) {\n return combinePlugins(plugins(toolbox), toolbox, pluginOptions)\n }\n\n if(isArray(plugins)) {\n const dest = pluginOptions.pluginLoadType === \"chain\" ? toolbox.getComponents() : {}\n\n return plugins\n .map(plugin => combinePlugins(plugin, toolbox, pluginOptions))\n .reduce(systemExtend, dest)\n }\n\n return {}\n}\n\nfunction callAfterLoad(plugins, system, { hasLoaded } = {}) {\n let calledSomething = hasLoaded\n if(isObject(plugins) && !isArray(plugins)) {\n if(typeof plugins.afterLoad === \"function\") {\n calledSomething = true\n wrapWithTryCatch(plugins.afterLoad).call(this, system)\n }\n }\n\n if(isFunc(plugins))\n return callAfterLoad.call(this, plugins(system), system, { hasLoaded: calledSomething })\n\n if(isArray(plugins)) {\n return plugins.map(plugin => callAfterLoad.call(this, plugin, system, { hasLoaded: calledSomething }))\n }\n\n return calledSomething\n}\n\n// Wraps deepExtend, to account for certain fields, being wrappers.\n// Ie: we need to convert some fields into arrays, and append to them.\n// Rather than overwrite\nfunction systemExtend(dest={}, src={}) {\n\n if(!isObject(dest)) {\n return {}\n }\n if(!isObject(src)) {\n return dest\n }\n\n // Wrap components\n // Parses existing components in the system, and prepares them for wrapping via getComponents\n if(src.wrapComponents) {\n objMap(src.wrapComponents, (wrapperFn, key) => {\n const ori = dest.components && dest.components[key]\n if(ori && Array.isArray(ori)) {\n dest.components[key] = ori.concat([wrapperFn])\n delete src.wrapComponents[key]\n } else if(ori) {\n dest.components[key] = [ori, wrapperFn]\n delete src.wrapComponents[key]\n }\n })\n\n if(!Object.keys(src.wrapComponents).length) {\n // only delete wrapComponents if we've matched all of our wrappers to components\n // this handles cases where the component to wrap may be out of our scope,\n // but a higher recursive `combinePlugins` call will be able to handle it.\n delete src.wrapComponents\n }\n }\n\n\n // Account for wrapActions, make it an array and append to it\n // Modifies `src`\n // 80% of this code is just safe traversal. We need to address that ( ie: use a lib )\n const { statePlugins } = dest\n if(isObject(statePlugins)) {\n for(let namespace in statePlugins) {\n const namespaceObj = statePlugins[namespace]\n if(!isObject(namespaceObj)) {\n continue\n }\n\n const { wrapActions, wrapSelectors } = namespaceObj\n\n // process action wrapping\n if (isObject(wrapActions)) {\n for(let actionName in wrapActions) {\n let action = wrapActions[actionName]\n\n // This should only happen if dest is the first plugin, since invocations after that will ensure its an array\n if(!Array.isArray(action)) {\n action = [action]\n wrapActions[actionName] = action // Put the value inside an array\n }\n\n if(src && src.statePlugins && src.statePlugins[namespace] && src.statePlugins[namespace].wrapActions && src.statePlugins[namespace].wrapActions[actionName]) {\n src.statePlugins[namespace].wrapActions[actionName] = wrapActions[actionName].concat(src.statePlugins[namespace].wrapActions[actionName])\n }\n\n }\n }\n\n // process selector wrapping\n if (isObject(wrapSelectors)) {\n for(let selectorName in wrapSelectors) {\n let selector = wrapSelectors[selectorName]\n\n // This should only happen if dest is the first plugin, since invocations after that will ensure its an array\n if(!Array.isArray(selector)) {\n selector = [selector]\n wrapSelectors[selectorName] = selector // Put the value inside an array\n }\n\n if(src && src.statePlugins && src.statePlugins[namespace] && src.statePlugins[namespace].wrapSelectors && src.statePlugins[namespace].wrapSelectors[selectorName]) {\n src.statePlugins[namespace].wrapSelectors[selectorName] = wrapSelectors[selectorName].concat(src.statePlugins[namespace].wrapSelectors[selectorName])\n }\n\n }\n }\n }\n }\n\n return deepExtend(dest, src)\n}\n\nfunction buildReducer(states) {\n let reducerObj = objMap(states, (val) => {\n return val.reducers\n })\n return allReducers(reducerObj)\n}\n\nfunction allReducers(reducerSystem) {\n let reducers = Object.keys(reducerSystem).reduce((obj, key) => {\n obj[key] = makeReducer(reducerSystem[key])\n return obj\n },{})\n\n if(!Object.keys(reducers).length) {\n return idFn\n }\n\n return combineReducers(reducers)\n}\n\nfunction makeReducer(reducerObj) {\n return (state = new Map(), action) => {\n if(!reducerObj)\n return state\n\n let redFn = (reducerObj[action.type])\n if(redFn) {\n const res = wrapWithTryCatch(redFn)(state, action)\n // If the try/catch wrapper kicks in, we'll get null back...\n // in that case, we want to avoid making any changes to state\n return res === null ? state : res\n }\n return state\n }\n}\n\nfunction wrapWithTryCatch(fn, {\n logErrors = true\n} = {}) {\n if(typeof fn !== \"function\") {\n return fn\n }\n\n return function(...args) {\n try {\n return fn.call(this, ...args)\n } catch(e) {\n if(logErrors) {\n console.error(e)\n }\n return null\n }\n }\n}\n\nfunction configureStore(rootReducer, initialState, getSystem) {\n const store = createStoreWithMiddleware(rootReducer, initialState, getSystem)\n\n // if (module.hot) {\n // // Enable Webpack hot module replacement for reducers\n // module.hot.accept(\"reducers/index\", () => {\n // const nextRootReducer = require(\"reducers/index\")\n // store.replaceReducer(nextRootReducer)\n // })\n // }\n\n return store\n}\n","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { opId } from \"swagger-client/es/helpers\"\nimport { Iterable, fromJS, Map } from \"immutable\"\n\nexport default class OperationContainer extends PureComponent {\n constructor(props, context) {\n super(props, context)\n\n const { tryItOutEnabled } = props.getConfigs()\n\n this.state = {\n tryItOutEnabled: tryItOutEnabled === true || tryItOutEnabled === \"true\",\n executeInProgress: false\n }\n }\n\n static propTypes = {\n op: PropTypes.instanceOf(Iterable).isRequired,\n tag: PropTypes.string.isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n operationId: PropTypes.string.isRequired,\n showSummary: PropTypes.bool.isRequired,\n isShown: PropTypes.bool.isRequired,\n jumpToKey: PropTypes.string.isRequired,\n allowTryItOut: PropTypes.bool,\n displayOperationId: PropTypes.bool,\n isAuthorized: PropTypes.bool,\n displayRequestDuration: PropTypes.bool,\n response: PropTypes.instanceOf(Iterable),\n request: PropTypes.instanceOf(Iterable),\n security: PropTypes.instanceOf(Iterable),\n isDeepLinkingEnabled: PropTypes.bool.isRequired,\n specPath: ImPropTypes.list.isRequired,\n getComponent: PropTypes.func.isRequired,\n authActions: PropTypes.object,\n oas3Actions: PropTypes.object,\n oas3Selectors: PropTypes.object,\n authSelectors: PropTypes.object,\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n fn: PropTypes.object.isRequired,\n getConfigs: PropTypes.func.isRequired\n }\n\n static defaultProps = {\n showSummary: true,\n response: null,\n allowTryItOut: true,\n displayOperationId: false,\n displayRequestDuration: false\n }\n\n mapStateToProps(nextState, props) {\n const { op, layoutSelectors, getConfigs } = props\n const { docExpansion, deepLinking, displayOperationId, displayRequestDuration, supportedSubmitMethods } = getConfigs()\n const showSummary = layoutSelectors.showSummary()\n const operationId = op.getIn([\"operation\", \"__originalOperationId\"]) || op.getIn([\"operation\", \"operationId\"]) || opId(op.get(\"operation\"), props.path, props.method) || op.get(\"id\")\n const isShownKey = [\"operations\", props.tag, operationId]\n const isDeepLinkingEnabled = deepLinking && deepLinking !== \"false\"\n const allowTryItOut = supportedSubmitMethods.indexOf(props.method) >= 0 && (typeof props.allowTryItOut === \"undefined\" ?\n props.specSelectors.allowTryItOutFor(props.path, props.method) : props.allowTryItOut)\n const security = op.getIn([\"operation\", \"security\"]) || props.specSelectors.security()\n\n return {\n operationId,\n isDeepLinkingEnabled,\n showSummary,\n displayOperationId,\n displayRequestDuration,\n allowTryItOut,\n security,\n isAuthorized: props.authSelectors.isAuthorized(security),\n isShown: layoutSelectors.isShown(isShownKey, docExpansion === \"full\" ),\n jumpToKey: `paths.${props.path}.${props.method}`,\n response: props.specSelectors.responseFor(props.path, props.method),\n request: props.specSelectors.requestFor(props.path, props.method)\n }\n }\n\n componentDidMount() {\n const { isShown } = this.props\n const resolvedSubtree = this.getResolvedSubtree()\n\n if(isShown && resolvedSubtree === undefined) {\n this.requestResolvedSubtree()\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n const { response, isShown } = nextProps\n const resolvedSubtree = this.getResolvedSubtree()\n\n if(response !== this.props.response) {\n this.setState({ executeInProgress: false })\n }\n\n if(isShown && resolvedSubtree === undefined) {\n this.requestResolvedSubtree()\n }\n }\n\n toggleShown =() => {\n let { layoutActions, tag, operationId, isShown } = this.props\n const resolvedSubtree = this.getResolvedSubtree()\n if(!isShown && resolvedSubtree === undefined) {\n // transitioning from collapsed to expanded\n this.requestResolvedSubtree()\n }\n layoutActions.show([\"operations\", tag, operationId], !isShown)\n }\n\n onCancelClick=() => {\n this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})\n }\n\n onTryoutClick =() => {\n this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})\n }\n\n onExecute = () => {\n this.setState({ executeInProgress: true })\n }\n\n getResolvedSubtree = () => {\n const {\n specSelectors,\n path,\n method,\n specPath\n } = this.props\n\n if(specPath) {\n return specSelectors.specResolvedSubtree(specPath.toJS())\n }\n\n return specSelectors.specResolvedSubtree([\"paths\", path, method])\n }\n\n requestResolvedSubtree = () => {\n const {\n specActions,\n path,\n method,\n specPath\n } = this.props\n\n\n if(specPath) {\n return specActions.requestResolvedSubtree(specPath.toJS())\n }\n\n return specActions.requestResolvedSubtree([\"paths\", path, method])\n }\n\n render() {\n let {\n op: unresolvedOp,\n tag,\n path,\n method,\n security,\n isAuthorized,\n operationId,\n showSummary,\n isShown,\n jumpToKey,\n allowTryItOut,\n response,\n request,\n displayOperationId,\n displayRequestDuration,\n isDeepLinkingEnabled,\n specPath,\n specSelectors,\n specActions,\n getComponent,\n getConfigs,\n layoutSelectors,\n layoutActions,\n authActions,\n authSelectors,\n oas3Actions,\n oas3Selectors,\n fn\n } = this.props\n\n const Operation = getComponent( \"operation\" )\n\n const resolvedSubtree = this.getResolvedSubtree() || Map()\n\n const operationProps = fromJS({\n op: resolvedSubtree,\n tag,\n path,\n summary: unresolvedOp.getIn([\"operation\", \"summary\"]) || \"\",\n deprecated: resolvedSubtree.get(\"deprecated\") || unresolvedOp.getIn([\"operation\", \"deprecated\"]) || false,\n method,\n security,\n isAuthorized,\n operationId,\n originalOperationId: resolvedSubtree.getIn([\"operation\", \"__originalOperationId\"]),\n showSummary,\n isShown,\n jumpToKey,\n allowTryItOut,\n request,\n displayOperationId,\n displayRequestDuration,\n isDeepLinkingEnabled,\n executeInProgress: this.state.executeInProgress,\n tryItOutEnabled: this.state.tryItOutEnabled\n })\n\n return (\n \n )\n }\n\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class App extends React.Component {\n\n getLayout() {\n let { getComponent, layoutSelectors } = this.props\n const layoutName = layoutSelectors.current()\n const Component = getComponent(layoutName, true)\n return Component ? Component : ()=>

No layout defined for "{layoutName}"

\n }\n\n render() {\n const Layout = this.getLayout()\n\n return (\n \n )\n }\n}\n\nApp.propTypes = {\n getComponent: PropTypes.func.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n}\n\nApp.defaultProps = {\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class AuthorizationPopup extends React.Component {\n close =() => {\n let { authActions } = this.props\n\n authActions.showDefinitions(false)\n }\n\n render() {\n let { authSelectors, authActions, getComponent, errSelectors, specSelectors, fn: { AST = {} } } = this.props\n let definitions = authSelectors.shownDefinitions()\n const Auths = getComponent(\"auths\")\n\n return (\n
\n
\n
\n
\n
\n
\n

Available authorizations

\n \n
\n
\n\n {\n definitions.valueSeq().map(( definition, key ) => {\n return \n })\n }\n
\n
\n
\n
\n
\n )\n }\n\n static propTypes = {\n fn: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n authSelectors: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n errSelectors: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class AuthorizeBtn extends React.Component {\n static propTypes = {\n onClick: PropTypes.func,\n isAuthorized: PropTypes.bool,\n showPopup: PropTypes.bool,\n getComponent: PropTypes.func.isRequired\n }\n\n render() {\n let { isAuthorized, showPopup, onClick, getComponent } = this.props\n\n //must be moved out of button component\n const AuthorizationPopup = getComponent(\"authorizationPopup\", true)\n\n return (\n
\n \n { showPopup && }\n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class AuthorizeBtnContainer extends React.Component {\n\n static propTypes = {\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n authSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n }\n\n render () {\n const { authActions, authSelectors, specSelectors, getComponent} = this.props\n \n const securityDefinitions = specSelectors.securityDefinitions()\n const authorizableDefinitions = authSelectors.definitionsToAuthorize()\n\n const AuthorizeBtn = getComponent(\"authorizeBtn\")\n\n return securityDefinitions ? (\n authActions.showDefinitions(authorizableDefinitions)}\n isAuthorized={!!authSelectors.authorized().size}\n showPopup={!!authSelectors.shownDefinitions()}\n getComponent={getComponent}\n />\n ) : null\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class AuthorizeOperationBtn extends React.Component {\n static propTypes = {\n isAuthorized: PropTypes.bool.isRequired,\n onClick: PropTypes.func\n }\n\n onClick =(e) => {\n e.stopPropagation()\n let { onClick } = this.props\n\n if(onClick) {\n onClick()\n }\n }\n\n render() {\n let { isAuthorized } = this.props\n\n return (\n \n\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class Auths extends React.Component {\n static propTypes = {\n definitions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n authSelectors: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired\n }\n\n constructor(props, context) {\n super(props, context)\n\n this.state = {}\n }\n\n onAuthChange =(auth) => {\n let { name } = auth\n\n this.setState({ [name]: auth })\n }\n\n submitAuth =(e) => {\n e.preventDefault()\n\n let { authActions } = this.props\n authActions.authorizeWithPersistOption(this.state)\n }\n\n logoutClick =(e) => {\n e.preventDefault()\n\n let { authActions, definitions } = this.props\n let auths = definitions.map( (val, key) => {\n return key\n }).toArray()\n\n this.setState(auths.reduce((prev, auth) => {\n prev[auth] = \"\"\n return prev\n }, {}))\n\n authActions.logoutWithPersistOption(auths)\n }\n\n close =(e) => {\n e.preventDefault()\n let { authActions } = this.props\n\n authActions.showDefinitions(false)\n }\n\n render() {\n let { definitions, getComponent, authSelectors, errSelectors } = this.props\n const AuthItem = getComponent(\"AuthItem\")\n const Oauth2 = getComponent(\"oauth2\", true)\n const Button = getComponent(\"Button\")\n\n let authorized = authSelectors.authorized()\n\n let authorizedAuth = definitions.filter( (definition, key) => {\n return !!authorized.get(key)\n })\n\n let nonOauthDefinitions = definitions.filter( schema => schema.get(\"type\") !== \"oauth2\")\n let oauthDefinitions = definitions.filter( schema => schema.get(\"type\") === \"oauth2\")\n\n return (\n
\n {\n !!nonOauthDefinitions.size && \n {\n nonOauthDefinitions.map( (schema, name) => {\n return \n }).toArray()\n }\n
\n {\n nonOauthDefinitions.size === authorizedAuth.size ? \n : \n }\n \n
\n \n }\n\n {\n oauthDefinitions && oauthDefinitions.size ?
\n
\n

Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.

\n

API requires the following scopes. Select which ones you want to grant to Swagger UI.

\n
\n {\n definitions.filter( schema => schema.get(\"type\") === \"oauth2\")\n .map( (schema, name) =>{\n return (
\n \n
)\n }\n ).toArray()\n }\n
: null\n }\n\n
\n )\n }\n\n static propTypes = {\n errSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n authSelectors: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n definitions: ImPropTypes.iterable.isRequired\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class Auths extends React.Component {\n static propTypes = {\n schema: ImPropTypes.orderedMap.isRequired,\n name: PropTypes.string.isRequired,\n onAuthChange: PropTypes.func.isRequired,\n authorized: ImPropTypes.orderedMap.isRequired\n }\n\n render() {\n let {\n schema,\n name,\n getComponent,\n onAuthChange,\n authorized,\n errSelectors\n } = this.props\n const ApiKeyAuth = getComponent(\"apiKeyAuth\")\n const BasicAuth = getComponent(\"basicAuth\")\n\n let authEl\n\n const type = schema.get(\"type\")\n\n switch(type) {\n case \"apiKey\": authEl = \n break\n case \"basic\": authEl = \n break\n default: authEl =
Unknown security definition type { type }
\n }\n\n return (
\n { authEl }\n
)\n }\n\n static propTypes = {\n errSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n authSelectors: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n definitions: ImPropTypes.iterable.isRequired\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class AuthError extends React.Component {\n\n static propTypes = {\n error: PropTypes.object.isRequired\n }\n\n render() {\n let { error } = this.props\n\n let level = error.get(\"level\")\n let message = error.get(\"message\")\n let source = error.get(\"source\")\n\n return (\n
\n { source } { level }\n { message }\n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class ApiKeyAuth extends React.Component {\n static propTypes = {\n authorized: PropTypes.object,\n getComponent: PropTypes.func.isRequired,\n errSelectors: PropTypes.object.isRequired,\n schema: PropTypes.object.isRequired,\n name: PropTypes.string.isRequired,\n onChange: PropTypes.func\n }\n\n constructor(props, context) {\n super(props, context)\n let { name, schema } = this.props\n let value = this.getValue()\n\n this.state = {\n name: name,\n schema: schema,\n value: value\n }\n }\n\n getValue () {\n let { name, authorized } = this.props\n\n return authorized && authorized.getIn([name, \"value\"])\n }\n\n onChange =(e) => {\n let { onChange } = this.props\n let value = e.target.value\n let newState = Object.assign({}, this.state, { value: value })\n\n this.setState(newState)\n onChange(newState)\n }\n\n render() {\n let { schema, getComponent, errSelectors, name } = this.props\n const Input = getComponent(\"Input\")\n const Row = getComponent(\"Row\")\n const Col = getComponent(\"Col\")\n const AuthError = getComponent(\"authError\")\n const Markdown = getComponent(\"Markdown\", true)\n const JumpToPath = getComponent(\"JumpToPath\", true)\n let value = this.getValue()\n let errors = errSelectors.allErrors().filter( err => err.get(\"authId\") === name)\n\n return (\n
\n

\n { name || schema.get(\"name\") } (apiKey)\n \n

\n { value &&
Authorized
}\n \n \n \n \n

Name: { schema.get(\"name\") }

\n
\n \n

In: { schema.get(\"in\") }

\n
\n \n \n {\n value ? ****** \n : \n }\n \n {\n errors.valueSeq().map( (error, key) => {\n return \n } )\n }\n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class BasicAuth extends React.Component {\n static propTypes = {\n authorized: PropTypes.object,\n getComponent: PropTypes.func.isRequired,\n schema: PropTypes.object.isRequired,\n onChange: PropTypes.func.isRequired\n }\n\n constructor(props, context) {\n super(props, context)\n let { schema, name } = this.props\n\n let value = this.getValue()\n let username = value.username\n\n this.state = {\n name: name,\n schema: schema,\n value: !username ? {} : {\n username: username\n }\n }\n }\n\n getValue () {\n let { authorized, name } = this.props\n\n return authorized && authorized.getIn([name, \"value\"]) || {}\n }\n\n onChange =(e) => {\n let { onChange } = this.props\n let { value, name } = e.target\n\n let newValue = this.state.value\n newValue[name] = value\n\n this.setState({ value: newValue })\n\n onChange(this.state)\n }\n\n render() {\n let { schema, getComponent, name, errSelectors } = this.props\n const Input = getComponent(\"Input\")\n const Row = getComponent(\"Row\")\n const Col = getComponent(\"Col\")\n const AuthError = getComponent(\"authError\")\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Markdown = getComponent(\"Markdown\", true)\n let username = this.getValue().username\n let errors = errSelectors.allErrors().filter( err => err.get(\"authId\") === name)\n\n return (\n
\n

Basic authorization

\n { username &&
Authorized
}\n \n \n \n \n \n {\n username ? { username } \n : \n }\n \n \n \n {\n username ? ****** \n : \n }\n \n {\n errors.valueSeq().map( (error, key) => {\n return \n } )\n }\n
\n )\n }\n\n static propTypes = {\n name: PropTypes.string.isRequired,\n errSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n onChange: PropTypes.func,\n schema: ImPropTypes.map,\n authorized: ImPropTypes.map\n }\n}\n","/**\n * @prettier\n */\n\nimport React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { stringify } from \"core/utils\"\n\nexport default function Example(props) {\n const { example, showValue, getComponent, getConfigs } = props\n\n const Markdown = getComponent(\"Markdown\", true)\n const HighlightCode = getComponent(\"highlightCode\")\n\n if(!example) return null\n\n return (\n
\n {example.get(\"description\") ? (\n
\n
Example Description
\n

\n \n

\n
\n ) : null}\n {showValue && example.has(\"value\") ? (\n
\n
Example Value
\n \n
\n ) : null}\n
\n )\n}\n\nExample.propTypes = {\n example: ImPropTypes.map.isRequired,\n showValue: PropTypes.bool,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.getConfigs,\n}\n","/**\n * @prettier\n */\n\nimport React from \"react\"\nimport Im from \"immutable\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class ExamplesSelect extends React.PureComponent {\n static propTypes = {\n examples: ImPropTypes.map.isRequired,\n onSelect: PropTypes.func,\n currentExampleKey: PropTypes.string,\n isModifiedValueAvailable: PropTypes.bool,\n isValueModified: PropTypes.bool,\n showLabels: PropTypes.bool,\n }\n\n static defaultProps = {\n examples: Im.Map({}),\n onSelect: (...args) =>\n console.log( // eslint-disable-line no-console\n // FIXME: remove before merging to master...\n `DEBUG: ExamplesSelect was not given an onSelect callback`,\n ...args\n ),\n currentExampleKey: null,\n showLabels: true,\n }\n\n _onSelect = (key, { isSyntheticChange = false } = {}) => {\n if (typeof this.props.onSelect === \"function\") {\n this.props.onSelect(key, {\n isSyntheticChange,\n })\n }\n }\n\n _onDomSelect = e => {\n if (typeof this.props.onSelect === \"function\") {\n const element = e.target.selectedOptions[0]\n const key = element.getAttribute(\"value\")\n\n this._onSelect(key, {\n isSyntheticChange: false,\n })\n }\n }\n\n getCurrentExample = () => {\n const { examples, currentExampleKey } = this.props\n\n const currentExamplePerProps = examples.get(currentExampleKey)\n\n const firstExamplesKey = examples.keySeq().first()\n const firstExample = examples.get(firstExamplesKey)\n\n return currentExamplePerProps || firstExample || Map({})\n }\n\n componentDidMount() {\n // this is the not-so-great part of ExamplesSelect... here we're\n // artificially kicking off an onSelect event in order to set a default\n // value in state. the consumer has the option to avoid this by checking\n // `isSyntheticEvent`, but we should really be doing this in a selector.\n // TODO: clean this up\n // FIXME: should this only trigger if `currentExamplesKey` is nullish?\n const { onSelect, examples } = this.props\n\n if (typeof onSelect === \"function\") {\n const firstExample = examples.first()\n const firstExampleKey = examples.keyOf(firstExample)\n\n this._onSelect(firstExampleKey, {\n isSyntheticChange: true,\n })\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n const { currentExampleKey, examples } = nextProps\n if (examples !== this.props.examples && !examples.has(currentExampleKey)) {\n // examples have changed from under us, and the currentExampleKey is no longer\n // valid.\n const firstExample = examples.first()\n const firstExampleKey = examples.keyOf(firstExample)\n\n this._onSelect(firstExampleKey, {\n isSyntheticChange: true,\n })\n }\n }\n\n render() {\n const {\n examples,\n currentExampleKey,\n isValueModified,\n isModifiedValueAvailable,\n showLabels,\n } = this.props\n\n return (\n
\n {\n showLabels ? (\n Examples: \n ) : null\n }\n \n {isModifiedValueAvailable ? (\n \n ) : null}\n {examples\n .map((example, exampleName) => {\n return (\n \n {example.get(\"summary\") || exampleName}\n \n )\n })\n .valueSeq()}\n \n
\n )\n }\n}\n","/**\n * @prettier\n */\nimport React from \"react\"\nimport { Map, List } from \"immutable\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nimport { stringify } from \"core/utils\"\n\n// This stateful component lets us avoid writing competing values (user\n// modifications vs example values) into global state, and the mess that comes\n// with that: tracking which of the two values are currently used for\n// Try-It-Out, which example a modified value came from, etc...\n//\n// The solution here is to retain the last user-modified value in\n// ExamplesSelectValueRetainer's component state, so that our global state can stay\n// clean, always simply being the source of truth for what value should be both\n// displayed to the user and used as a value during request execution.\n//\n// This approach/tradeoff was chosen in order to encapsulate the particular\n// logic of Examples within the Examples component tree, and to avoid\n// regressions within our current implementation elsewhere (non-Examples\n// definitions, OpenAPI 2.0, etc). A future refactor to global state might make\n// this component unnecessary.\n//\n// TL;DR: this is not our usual approach, but the choice was made consciously.\n\n// Note that `currentNamespace` isn't currently used anywhere!\n\nconst stringifyUnlessList = input =>\n List.isList(input) ? input : stringify(input)\n\nexport default class ExamplesSelectValueRetainer extends React.PureComponent {\n static propTypes = {\n examples: ImPropTypes.map,\n onSelect: PropTypes.func,\n updateValue: PropTypes.func, // mechanism to update upstream value\n userHasEditedBody: PropTypes.bool,\n getComponent: PropTypes.func.isRequired,\n currentUserInputValue: PropTypes.any,\n currentKey: PropTypes.string,\n currentNamespace: PropTypes.string,\n setRetainRequestBodyValueFlag: PropTypes.func.isRequired,\n // (also proxies props for Examples)\n }\n\n static defaultProps = {\n userHasEditedBody: false,\n examples: Map({}),\n currentNamespace: \"__DEFAULT__NAMESPACE__\",\n setRetainRequestBodyValueFlag: () => {\n // NOOP\n },\n onSelect: (...args) =>\n console.log( // eslint-disable-line no-console\n \"ExamplesSelectValueRetainer: no `onSelect` function was provided\",\n ...args\n ),\n updateValue: (...args) =>\n console.log( // eslint-disable-line no-console\n \"ExamplesSelectValueRetainer: no `updateValue` function was provided\",\n ...args\n ),\n }\n\n constructor(props) {\n super(props)\n\n const valueFromExample = this._getCurrentExampleValue()\n\n this.state = {\n // user edited: last value that came from the world around us, and didn't\n // match the current example's value\n // internal: last value that came from user selecting an Example\n [props.currentNamespace]: Map({\n lastUserEditedValue: this.props.currentUserInputValue,\n lastDownstreamValue: valueFromExample,\n isModifiedValueSelected:\n // valueFromExample !== undefined &&\n this.props.userHasEditedBody ||\n this.props.currentUserInputValue !== valueFromExample,\n }),\n }\n }\n\n componentWillUnmount() {\n this.props.setRetainRequestBodyValueFlag(false)\n }\n\n _getStateForCurrentNamespace = () => {\n const { currentNamespace } = this.props\n\n return (this.state[currentNamespace] || Map()).toObject()\n }\n\n _setStateForCurrentNamespace = obj => {\n const { currentNamespace } = this.props\n\n return this._setStateForNamespace(currentNamespace, obj)\n }\n\n _setStateForNamespace = (namespace, obj) => {\n const oldStateForNamespace = this.state[namespace] || Map()\n const newStateForNamespace = oldStateForNamespace.mergeDeep(obj)\n return this.setState({\n [namespace]: newStateForNamespace,\n })\n }\n\n _isCurrentUserInputSameAsExampleValue = () => {\n const { currentUserInputValue } = this.props\n\n const valueFromExample = this._getCurrentExampleValue()\n\n return valueFromExample === currentUserInputValue\n }\n\n _getValueForExample = (exampleKey, props) => {\n // props are accepted so that this can be used in UNSAFE_componentWillReceiveProps,\n // which has access to `nextProps`\n const { examples } = props || this.props\n return stringifyUnlessList(\n (examples || Map({})).getIn([exampleKey, \"value\"])\n )\n }\n\n _getCurrentExampleValue = props => {\n // props are accepted so that this can be used in UNSAFE_componentWillReceiveProps,\n // which has access to `nextProps`\n const { currentKey } = props || this.props\n return this._getValueForExample(currentKey, props || this.props)\n }\n\n _onExamplesSelect = (key, { isSyntheticChange } = {}, ...otherArgs) => {\n const {\n onSelect,\n updateValue,\n currentUserInputValue,\n userHasEditedBody,\n } = this.props\n const { lastUserEditedValue } = this._getStateForCurrentNamespace()\n\n const valueFromExample = this._getValueForExample(key)\n\n if (key === \"__MODIFIED__VALUE__\") {\n updateValue(stringifyUnlessList(lastUserEditedValue))\n return this._setStateForCurrentNamespace({\n isModifiedValueSelected: true,\n })\n }\n\n if (typeof onSelect === \"function\") {\n onSelect(key, { isSyntheticChange }, ...otherArgs)\n }\n\n this._setStateForCurrentNamespace({\n lastDownstreamValue: valueFromExample,\n isModifiedValueSelected:\n (isSyntheticChange && userHasEditedBody) ||\n (!!currentUserInputValue && currentUserInputValue !== valueFromExample),\n })\n\n // we never want to send up value updates from synthetic changes\n if (isSyntheticChange) return\n\n if (typeof updateValue === \"function\") {\n updateValue(stringifyUnlessList(valueFromExample))\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n // update `lastUserEditedValue` as new currentUserInput values come in\n\n const {\n currentUserInputValue: newValue,\n examples,\n onSelect,\n userHasEditedBody,\n } = nextProps\n\n const {\n lastUserEditedValue,\n lastDownstreamValue,\n } = this._getStateForCurrentNamespace()\n\n const valueFromCurrentExample = this._getValueForExample(\n nextProps.currentKey,\n nextProps\n )\n\n const examplesMatchingNewValue = examples.filter(\n (example) =>\n example.get(\"value\") === newValue ||\n // sometimes data is stored as a string (e.g. in Request Bodies), so\n // let's check against a stringified version of our example too\n stringify(example.get(\"value\")) === newValue\n )\n\n if (examplesMatchingNewValue.size) {\n let key\n if(examplesMatchingNewValue.has(nextProps.currentKey))\n {\n key = nextProps.currentKey\n } else {\n key = examplesMatchingNewValue.keySeq().first()\n }\n onSelect(key, {\n isSyntheticChange: true,\n })\n } else if (\n newValue !== this.props.currentUserInputValue && // value has changed\n newValue !== lastUserEditedValue && // value isn't already tracked\n newValue !== lastDownstreamValue // value isn't what we've seen on the other side\n ) {\n this.props.setRetainRequestBodyValueFlag(true)\n this._setStateForNamespace(nextProps.currentNamespace, {\n lastUserEditedValue: nextProps.currentUserInputValue,\n isModifiedValueSelected:\n userHasEditedBody || newValue !== valueFromCurrentExample,\n })\n }\n }\n\n render() {\n const {\n currentUserInputValue,\n examples,\n currentKey,\n getComponent,\n userHasEditedBody,\n } = this.props\n const {\n lastDownstreamValue,\n lastUserEditedValue,\n isModifiedValueSelected,\n } = this._getStateForCurrentNamespace()\n\n const ExamplesSelect = getComponent(\"ExamplesSelect\")\n\n return (\n \n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport oauth2Authorize from \"core/oauth2-authorize\"\n\nexport default class Oauth2 extends React.Component {\n static propTypes = {\n name: PropTypes.string,\n authorized: PropTypes.object,\n getComponent: PropTypes.func.isRequired,\n schema: PropTypes.object.isRequired,\n authSelectors: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n errSelectors: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n errActions: PropTypes.object.isRequired,\n getConfigs: PropTypes.any\n }\n\n constructor(props, context) {\n super(props, context)\n let { name, schema, authorized, authSelectors } = this.props\n let auth = authorized && authorized.get(name)\n let authConfigs = authSelectors.getConfigs() || {}\n let username = auth && auth.get(\"username\") || \"\"\n let clientId = auth && auth.get(\"clientId\") || authConfigs.clientId || \"\"\n let clientSecret = auth && auth.get(\"clientSecret\") || authConfigs.clientSecret || \"\"\n let passwordType = auth && auth.get(\"passwordType\") || \"basic\"\n let scopes = auth && auth.get(\"scopes\") || authConfigs.scopes || []\n if (typeof scopes === \"string\") {\n scopes = scopes.split(authConfigs.scopeSeparator || \" \")\n }\n\n this.state = {\n appName: authConfigs.appName,\n name: name,\n schema: schema,\n scopes: scopes,\n clientId: clientId,\n clientSecret: clientSecret,\n username: username,\n password: \"\",\n passwordType: passwordType\n }\n }\n\n close = (e) => {\n e.preventDefault()\n let { authActions } = this.props\n\n authActions.showDefinitions(false)\n }\n\n authorize =() => {\n let { authActions, errActions, getConfigs, authSelectors, oas3Selectors } = this.props\n let configs = getConfigs()\n let authConfigs = authSelectors.getConfigs()\n\n errActions.clear({authId: name,type: \"auth\", source: \"auth\"})\n oauth2Authorize({\n auth: this.state,\n currentServer: oas3Selectors.serverEffectiveValue(oas3Selectors.selectedServer()),\n authActions,\n errActions,\n configs,\n authConfigs\n })\n }\n\n onScopeChange =(e) => {\n let { target } = e\n let { checked } = target\n let scope = target.dataset.value\n\n if ( checked && this.state.scopes.indexOf(scope) === -1 ) {\n let newScopes = this.state.scopes.concat([scope])\n this.setState({ scopes: newScopes })\n } else if ( !checked && this.state.scopes.indexOf(scope) > -1) {\n this.setState({ scopes: this.state.scopes.filter((val) => val !== scope) })\n }\n }\n\n onInputChange =(e) => {\n let { target : { dataset : { name }, value } } = e\n let state = {\n [name]: value\n }\n\n this.setState(state)\n }\n\n selectScopes =(e) => {\n if (e.target.dataset.all) {\n this.setState({\n scopes: Array.from((this.props.schema.get(\"allowedScopes\") || this.props.schema.get(\"scopes\")).keys())\n })\n } else {\n this.setState({ scopes: [] })\n }\n }\n\n logout =(e) => {\n e.preventDefault()\n let { authActions, errActions, name } = this.props\n\n errActions.clear({authId: name, type: \"auth\", source: \"auth\"})\n authActions.logoutWithPersistOption([ name ])\n }\n\n render() {\n let {\n schema, getComponent, authSelectors, errSelectors, name, specSelectors\n } = this.props\n const Input = getComponent(\"Input\")\n const Row = getComponent(\"Row\")\n const Col = getComponent(\"Col\")\n const Button = getComponent(\"Button\")\n const AuthError = getComponent(\"authError\")\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Markdown = getComponent(\"Markdown\", true)\n const InitializedInput = getComponent(\"InitializedInput\")\n\n const { isOAS3 } = specSelectors\n\n let oidcUrl = isOAS3() ? schema.get(\"openIdConnectUrl\") : null\n\n // Auth type consts\n const AUTH_FLOW_IMPLICIT = \"implicit\"\n const AUTH_FLOW_PASSWORD = \"password\"\n const AUTH_FLOW_ACCESS_CODE = isOAS3() ? (oidcUrl ? \"authorization_code\" : \"authorizationCode\") : \"accessCode\"\n const AUTH_FLOW_APPLICATION = isOAS3() ? (oidcUrl ? \"client_credentials\" : \"clientCredentials\") : \"application\"\n\n let authConfigs = authSelectors.getConfigs() || {}\n let isPkceCodeGrant = !!authConfigs.usePkceWithAuthorizationCodeGrant\n\n let flow = schema.get(\"flow\")\n let flowToDisplay = flow === AUTH_FLOW_ACCESS_CODE && isPkceCodeGrant ? flow + \" with PKCE\" : flow\n let scopes = schema.get(\"allowedScopes\") || schema.get(\"scopes\")\n let authorizedAuth = authSelectors.authorized().get(name)\n let isAuthorized = !!authorizedAuth\n let errors = errSelectors.allErrors().filter( err => err.get(\"authId\") === name)\n let isValid = !errors.filter( err => err.get(\"source\") === \"validation\").size\n let description = schema.get(\"description\")\n\n return (\n
\n

{name} (OAuth2, { flowToDisplay })

\n { !this.state.appName ? null :
Application: { this.state.appName }
}\n { description && }\n\n { isAuthorized &&
Authorized
}\n\n { oidcUrl &&

OpenID Connect URL: { oidcUrl }

}\n { ( flow === AUTH_FLOW_IMPLICIT || flow === AUTH_FLOW_ACCESS_CODE ) &&

Authorization URL: { schema.get(\"authorizationUrl\") }

}\n { ( flow === AUTH_FLOW_PASSWORD || flow === AUTH_FLOW_ACCESS_CODE || flow === AUTH_FLOW_APPLICATION ) &&

Token URL: { schema.get(\"tokenUrl\") }

}\n

Flow: { flowToDisplay }

\n\n {\n flow !== AUTH_FLOW_PASSWORD ? null\n : \n \n \n {\n isAuthorized ? { this.state.username } \n : \n \n \n }\n \n {\n\n }\n \n \n {\n isAuthorized ? ****** \n : \n \n \n }\n \n \n \n {\n isAuthorized ? { this.state.passwordType } \n : \n \n \n }\n \n \n }\n {\n ( flow === AUTH_FLOW_APPLICATION || flow === AUTH_FLOW_IMPLICIT || flow === AUTH_FLOW_ACCESS_CODE || flow === AUTH_FLOW_PASSWORD ) &&\n ( !isAuthorized || isAuthorized && this.state.clientId) && \n \n {\n isAuthorized ? ****** \n : \n \n \n }\n \n }\n\n {\n ( (flow === AUTH_FLOW_APPLICATION || flow === AUTH_FLOW_ACCESS_CODE || flow === AUTH_FLOW_PASSWORD) && !isPkceCodeGrant && \n \n {\n isAuthorized ? ****** \n : \n \n \n }\n\n \n )}\n\n {\n !isAuthorized && scopes && scopes.size ?
\n

\n Scopes:\n select all\n select none\n

\n { scopes.map((description, name) => {\n return (\n \n
\n \n \n
\n
\n )\n }).toArray()\n }\n
: null\n }\n\n {\n errors.valueSeq().map( (error, key) => {\n return \n } )\n }\n
\n { isValid &&\n ( isAuthorized ? \n : \n )\n }\n \n
\n\n
\n )\n }\n}\n","import parseUrl from \"url-parse\"\nimport Im from \"immutable\"\nimport { btoa, sanitizeUrl, generateCodeVerifier, createCodeChallenge } from \"core/utils\"\n\nexport default function authorize ( { auth, authActions, errActions, configs, authConfigs={}, currentServer } ) {\n let { schema, scopes, name, clientId } = auth\n let flow = schema.get(\"flow\")\n let query = []\n\n switch (flow) {\n case \"password\":\n authActions.authorizePassword(auth)\n return\n\n case \"application\":\n authActions.authorizeApplication(auth)\n return\n\n case \"accessCode\":\n query.push(\"response_type=code\")\n break\n\n case \"implicit\":\n query.push(\"response_type=token\")\n break\n\n case \"clientCredentials\":\n case \"client_credentials\":\n // OAS3\n authActions.authorizeApplication(auth)\n return\n\n case \"authorizationCode\":\n case \"authorization_code\":\n // OAS3\n query.push(\"response_type=code\")\n break\n }\n\n if (typeof clientId === \"string\") {\n query.push(\"client_id=\" + encodeURIComponent(clientId))\n }\n\n let redirectUrl = configs.oauth2RedirectUrl\n\n // todo move to parser\n if (typeof redirectUrl === \"undefined\") {\n errActions.newAuthErr( {\n authId: name,\n source: \"validation\",\n level: \"error\",\n message: \"oauth2RedirectUrl configuration is not passed. Oauth2 authorization cannot be performed.\"\n })\n return\n }\n query.push(\"redirect_uri=\" + encodeURIComponent(redirectUrl))\n\n let scopesArray = []\n if (Array.isArray(scopes)) {\n scopesArray = scopes\n } else if (Im.List.isList(scopes)) {\n scopesArray = scopes.toArray()\n }\n\n if (scopesArray.length > 0) {\n let scopeSeparator = authConfigs.scopeSeparator || \" \"\n\n query.push(\"scope=\" + encodeURIComponent(scopesArray.join(scopeSeparator)))\n }\n\n let state = btoa(new Date())\n\n query.push(\"state=\" + encodeURIComponent(state))\n\n if (typeof authConfigs.realm !== \"undefined\") {\n query.push(\"realm=\" + encodeURIComponent(authConfigs.realm))\n }\n\n if ((flow === \"authorizationCode\" || flow === \"authorization_code\" || flow === \"accessCode\") && authConfigs.usePkceWithAuthorizationCodeGrant) {\n const codeVerifier = generateCodeVerifier()\n const codeChallenge = createCodeChallenge(codeVerifier)\n\n query.push(\"code_challenge=\" + codeChallenge)\n query.push(\"code_challenge_method=S256\")\n\n // storing the Code Verifier so it can be sent to the token endpoint\n // when exchanging the Authorization Code for an Access Token\n auth.codeVerifier = codeVerifier\n }\n\n let { additionalQueryStringParams } = authConfigs\n\n for (let key in additionalQueryStringParams) {\n if (typeof additionalQueryStringParams[key] !== \"undefined\") {\n query.push([key, additionalQueryStringParams[key]].map(encodeURIComponent).join(\"=\"))\n }\n }\n\n const authorizationUrl = schema.get(\"authorizationUrl\")\n let sanitizedAuthorizationUrl\n if (currentServer) {\n // OpenAPI 3\n sanitizedAuthorizationUrl = parseUrl(\n sanitizeUrl(authorizationUrl),\n currentServer,\n true\n ).toString()\n } else {\n sanitizedAuthorizationUrl = sanitizeUrl(authorizationUrl)\n }\n let url = [sanitizedAuthorizationUrl, query.join(\"&\")].join(authorizationUrl.indexOf(\"?\") === -1 ? \"?\" : \"&\")\n\n // pass action authorizeOauth2 and authentication data through window\n // to authorize with oauth2\n\n let callback\n if (flow === \"implicit\") {\n callback = authActions.preAuthorizeImplicit\n } else if (authConfigs.useBasicAuthenticationWithAccessCodeGrant) {\n callback = authActions.authorizeAccessCodeWithBasicAuthentication\n } else {\n callback = authActions.authorizeAccessCodeWithFormParams\n }\n\n authActions.authPopup(url, {\n auth: auth,\n state: state,\n redirectUrl: redirectUrl,\n callback: callback,\n errCb: errActions.newAuthErr\n })\n}\n","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class Clear extends Component {\n\n onClick =() => {\n let { specActions, path, method } = this.props\n specActions.clearResponse( path, method )\n specActions.clearRequest( path, method )\n }\n\n render(){\n return (\n \n )\n }\n\n static propTypes = {\n specActions: PropTypes.object.isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { Iterable } from \"immutable\"\n\nconst Headers = ( { headers } )=>{\n return (\n
\n
Response headers
\n
{headers}
\n
)\n}\nHeaders.propTypes = {\n headers: PropTypes.array.isRequired\n}\n\nconst Duration = ( { duration } ) => {\n return (\n
\n
Request duration
\n
{duration} ms
\n
\n )\n}\nDuration.propTypes = {\n duration: PropTypes.number.isRequired\n}\n\n\nexport default class LiveResponse extends React.Component {\n static propTypes = {\n response: PropTypes.instanceOf(Iterable).isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n displayRequestDuration: PropTypes.bool.isRequired,\n specSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired\n }\n\n shouldComponentUpdate(nextProps) {\n // BUG: props.response is always coming back as a new Immutable instance\n // same issue as responses.jsx (tryItOutResponse)\n return this.props.response !== nextProps.response\n || this.props.path !== nextProps.path\n || this.props.method !== nextProps.method\n || this.props.displayRequestDuration !== nextProps.displayRequestDuration\n }\n\n render() {\n const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props\n const { showMutatedRequest, requestSnippetsEnabled } = getConfigs()\n\n const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)\n const status = response.get(\"status\")\n const url = curlRequest.get(\"url\")\n const headers = response.get(\"headers\").toJS()\n const notDocumented = response.get(\"notDocumented\")\n const isError = response.get(\"error\")\n const body = response.get(\"text\")\n const duration = response.get(\"duration\")\n const headersKeys = Object.keys(headers)\n const contentType = headers[\"content-type\"] || headers[\"Content-Type\"]\n\n const ResponseBody = getComponent(\"responseBody\")\n const returnObject = headersKeys.map(key => {\n var joinedHeaders = Array.isArray(headers[key]) ? headers[key].join() : headers[key]\n return {key}: {joinedHeaders} \n })\n const hasHeaders = returnObject.length !== 0\n const Markdown = getComponent(\"Markdown\", true)\n const RequestSnippets = getComponent(\"RequestSnippets\", true)\n const Curl = getComponent(\"curl\")\n\n return (\n
\n { curlRequest && (requestSnippetsEnabled === true || requestSnippetsEnabled === \"true\"\n ? \n : ) }\n { url &&
\n
\n

Request URL

\n
{url}
\n
\n
\n }\n

Server response

\n \n \n \n \n \n \n \n \n \n \n \n \n \n
CodeDetails
\n { status }\n {\n notDocumented ?
\n Undocumented \n
\n : null\n }\n
\n {\n isError ? \n : null\n }\n {\n body ? \n : null\n }\n {\n hasHeaders ? : null\n }\n {\n displayRequestDuration && duration ? : null\n }\n
\n
\n )\n }\n\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n response: ImPropTypes.map\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Im from \"immutable\"\n\nconst SWAGGER2_OPERATION_METHODS = [\n \"get\", \"put\", \"post\", \"delete\", \"options\", \"head\", \"patch\"\n]\n\nconst OAS3_OPERATION_METHODS = SWAGGER2_OPERATION_METHODS.concat([\"trace\"])\n\n\nexport default class Operations extends React.Component {\n\n static propTypes = {\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n oas3Selectors: PropTypes.func.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n authActions: PropTypes.object.isRequired,\n authSelectors: PropTypes.object.isRequired,\n getConfigs: PropTypes.func.isRequired,\n fn: PropTypes.func.isRequired\n };\n\n render() {\n let {\n specSelectors,\n } = this.props\n\n const taggedOps = specSelectors.taggedOperations()\n\n if(taggedOps.size === 0) {\n return

No operations defined in spec!

\n }\n\n return (\n
\n { taggedOps.map(this.renderOperationTag).toArray() }\n { taggedOps.size < 1 ?

No operations defined in spec!

: null }\n
\n )\n }\n\n renderOperationTag = (tagObj, tag) => {\n const {\n specSelectors,\n getComponent,\n oas3Selectors,\n layoutSelectors,\n layoutActions,\n getConfigs,\n } = this.props\n const OperationContainer = getComponent(\"OperationContainer\", true)\n const OperationTag = getComponent(\"OperationTag\")\n const operations = tagObj.get(\"operations\")\n return (\n \n
\n {\n operations.map(op => {\n const path = op.get(\"path\")\n const method = op.get(\"method\")\n const specPath = Im.List([\"paths\", path, method])\n\n\n // FIXME: (someday) this logic should probably be in a selector,\n // but doing so would require further opening up\n // selectors to the plugin system, to allow for dynamic\n // overriding of low-level selectors that other selectors\n // rely on. --KS, 12/17\n const validMethods = specSelectors.isOAS3() ?\n OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS\n\n if (validMethods.indexOf(method) === -1) {\n return null\n }\n\n return (\n \n )\n }).toArray()\n }\n
\n \n )\n }\n\n}\n\nOperations.propTypes = {\n layoutActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n fn: PropTypes.object.isRequired\n}\n","export function isAbsoluteUrl(url) {\n return url.match(/^(?:[a-z]+:)?\\/\\//i) // Matches http://, HTTP://, https://, ftp://, //example.com,\n}\n\nexport function addProtocol(url) {\n if (!url.match(/^\\/\\//i)) return url // Checks if protocol is missing e.g. //example.com\n\n return `${window.location.protocol}${url}`\n}\n\nexport function buildBaseUrl(selectedServer, specUrl) {\n if (!selectedServer) return specUrl\n if (isAbsoluteUrl(selectedServer)) return addProtocol(selectedServer)\n\n return new URL(selectedServer, specUrl).href\n}\n\nexport function buildUrl(url, specUrl, { selectedServer=\"\" } = {}) {\n if (!url) return undefined\n if (isAbsoluteUrl(url)) return url\n\n const baseUrl = buildBaseUrl(selectedServer, specUrl)\n if (!isAbsoluteUrl(baseUrl)) {\n return new URL(url, window.location.href).href\n }\n return new URL(url, baseUrl).href\n}\n\n/**\n * Safe version of buildUrl function. `selectedServer` can contain server variables\n * which can fail the URL resolution.\n */\nexport function safeBuildUrl(url, specUrl, { selectedServer=\"\" } = {}) {\n try {\n return buildUrl(url, specUrl, { selectedServer })\n } catch {\n return undefined\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport Im from \"immutable\"\nimport { createDeepLinkPath, escapeDeepLinkPath, sanitizeUrl } from \"core/utils\"\nimport { safeBuildUrl } from \"core/utils/url\"\nimport { isFunc } from \"core/utils\"\n\nexport default class OperationTag extends React.Component {\n\n static defaultProps = {\n tagObj: Im.fromJS({}),\n tag: \"\",\n }\n\n static propTypes = {\n tagObj: ImPropTypes.map.isRequired,\n tag: PropTypes.string.isRequired,\n\n oas3Selectors: PropTypes.func.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n\n getConfigs: PropTypes.func.isRequired,\n getComponent: PropTypes.func.isRequired,\n\n specUrl: PropTypes.string.isRequired,\n\n children: PropTypes.element,\n }\n\n render() {\n const {\n tagObj,\n tag,\n children,\n oas3Selectors,\n layoutSelectors,\n layoutActions,\n getConfigs,\n getComponent,\n specUrl,\n } = this.props\n\n let {\n docExpansion,\n deepLinking,\n } = getConfigs()\n\n const isDeepLinkingEnabled = deepLinking && deepLinking !== \"false\"\n\n const Collapse = getComponent(\"Collapse\")\n const Markdown = getComponent(\"Markdown\", true)\n const DeepLink = getComponent(\"DeepLink\")\n const Link = getComponent(\"Link\")\n\n let tagDescription = tagObj.getIn([\"tagDetails\", \"description\"], null)\n let tagExternalDocsDescription = tagObj.getIn([\"tagDetails\", \"externalDocs\", \"description\"])\n let rawTagExternalDocsUrl = tagObj.getIn([\"tagDetails\", \"externalDocs\", \"url\"])\n let tagExternalDocsUrl\n if (isFunc(oas3Selectors) && isFunc(oas3Selectors.selectedServer)) {\n tagExternalDocsUrl = safeBuildUrl(rawTagExternalDocsUrl, specUrl, { selectedServer: oas3Selectors.selectedServer() })\n } else {\n tagExternalDocsUrl = rawTagExternalDocsUrl\n }\n\n let isShownKey = [\"operations-tag\", tag]\n let showTag = layoutSelectors.isShown(isShownKey, docExpansion === \"full\" || docExpansion === \"list\")\n\n return (\n
\n\n layoutActions.show(isShownKey, !showTag)}\n className={!tagDescription ? \"opblock-tag no-desc\" : \"opblock-tag\"}\n id={isShownKey.map(v => escapeDeepLinkPath(v)).join(\"-\")}\n data-tag={tag}\n data-is-open={showTag}\n >\n \n {!tagDescription ? :\n \n \n \n }\n\n {!tagExternalDocsDescription ? null :\n
\n \n {tagExternalDocsDescription}\n {tagExternalDocsUrl ? \": \" : null}\n {tagExternalDocsUrl ?\n e.stopPropagation()}\n target=\"_blank\"\n >{tagExternalDocsUrl} : null\n }\n \n
\n }\n\n\n layoutActions.show(isShownKey, !showTag)}>\n\n \n \n \n \n \n\n \n {children}\n \n
\n )\n }\n}\n","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { getList } from \"core/utils\"\nimport { getExtensions, sanitizeUrl, escapeDeepLinkPath } from \"core/utils\"\nimport { safeBuildUrl } from \"core/utils/url\"\nimport { Iterable, List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\n\nexport default class Operation extends PureComponent {\n static propTypes = {\n specPath: ImPropTypes.list.isRequired,\n operation: PropTypes.instanceOf(Iterable).isRequired,\n summary: PropTypes.string,\n response: PropTypes.instanceOf(Iterable),\n request: PropTypes.instanceOf(Iterable),\n\n toggleShown: PropTypes.func.isRequired,\n onTryoutClick: PropTypes.func.isRequired,\n onCancelClick: PropTypes.func.isRequired,\n onExecute: PropTypes.func.isRequired,\n\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n authActions: PropTypes.object,\n authSelectors: PropTypes.object,\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n fn: PropTypes.object.isRequired\n }\n\n static defaultProps = {\n operation: null,\n response: null,\n request: null,\n specPath: List(),\n summary: \"\"\n }\n\n render() {\n let {\n specPath,\n response,\n request,\n toggleShown,\n onTryoutClick,\n onCancelClick,\n onExecute,\n fn,\n getComponent,\n getConfigs,\n specActions,\n specSelectors,\n authActions,\n authSelectors,\n oas3Actions,\n oas3Selectors\n } = this.props\n let operationProps = this.props.operation\n\n let {\n deprecated,\n isShown,\n path,\n method,\n op,\n tag,\n operationId,\n allowTryItOut,\n displayRequestDuration,\n tryItOutEnabled,\n executeInProgress\n } = operationProps.toJS()\n\n let {\n description,\n externalDocs,\n schemes\n } = op\n\n const externalDocsUrl = externalDocs ? safeBuildUrl(externalDocs.url, specSelectors.url(), { selectedServer: oas3Selectors.selectedServer() }) : \"\"\n let operation = operationProps.getIn([\"op\"])\n let responses = operation.get(\"responses\")\n let parameters = getList(operation, [\"parameters\"])\n let operationScheme = specSelectors.operationScheme(path, method)\n let isShownKey = [\"operations\", tag, operationId]\n let extensions = getExtensions(operation)\n\n const Responses = getComponent(\"responses\")\n const Parameters = getComponent( \"parameters\" )\n const Execute = getComponent( \"execute\" )\n const Clear = getComponent( \"clear\" )\n const Collapse = getComponent( \"Collapse\" )\n const Markdown = getComponent(\"Markdown\", true)\n const Schemes = getComponent( \"schemes\" )\n const OperationServers = getComponent( \"OperationServers\" )\n const OperationExt = getComponent( \"OperationExt\" )\n const OperationSummary = getComponent( \"OperationSummary\" )\n const Link = getComponent( \"Link\" )\n\n const { showExtensions } = getConfigs()\n\n // Merge in Live Response\n if(responses && response && response.size > 0) {\n let notDocumented = !responses.get(String(response.get(\"status\"))) && !responses.get(\"default\")\n response = response.set(\"notDocumented\", notDocumented)\n }\n\n let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )\n\n return (\n
\n \n \n
\n { (operation && operation.size) || operation === null ? null :\n \n }\n { deprecated &&

Warning: Deprecated

}\n { description &&\n
\n
\n \n
\n
\n }\n {\n externalDocsUrl ?\n
\n

Find more details

\n
\n \n \n \n {externalDocsUrl}\n
\n
: null\n }\n\n { !operation || !operation.size ? null :\n \n }\n\n { !tryItOutEnabled ? null :\n \n }\n\n {!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ?
\n \n
: null\n }\n\n
\n { !tryItOutEnabled || !allowTryItOut ? null :\n\n \n }\n\n { (!tryItOutEnabled || !response || !allowTryItOut) ? null :\n \n }\n
\n\n {executeInProgress ?
: null}\n\n { !responses ? null :\n \n }\n\n { !showExtensions || !extensions.size ? null :\n \n }\n
\n
\n
\n )\n }\n\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_toString_da931f05__[\"default\"] });","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { Iterable, List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport toString from \"lodash/toString\"\n\n\nexport default class OperationSummary extends PureComponent {\n\n static propTypes = {\n specPath: ImPropTypes.list.isRequired,\n operationProps: PropTypes.instanceOf(Iterable).isRequired,\n isShown: PropTypes.bool.isRequired,\n toggleShown: PropTypes.func.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n authActions: PropTypes.object,\n authSelectors: PropTypes.object,\n }\n\n static defaultProps = {\n operationProps: null,\n specPath: List(),\n summary: \"\"\n }\n\n render() {\n\n let {\n isShown,\n toggleShown,\n getComponent,\n authActions,\n authSelectors,\n operationProps,\n specPath,\n } = this.props\n\n let {\n summary,\n isAuthorized,\n method,\n op,\n showSummary,\n path,\n operationId,\n originalOperationId,\n displayOperationId,\n } = operationProps.toJS()\n\n let {\n summary: resolvedSummary,\n } = op\n\n let security = operationProps.get(\"security\")\n\n const AuthorizeOperationBtn = getComponent(\"authorizeOperationBtn\")\n const OperationSummaryMethod = getComponent(\"OperationSummaryMethod\")\n const OperationSummaryPath = getComponent(\"OperationSummaryPath\")\n const JumpToPath = getComponent(\"JumpToPath\", true)\n\n const hasSecurity = security && !!security.count()\n const securityIsOptional = hasSecurity && security.size === 1 && security.first().isEmpty()\n const allowAnonymous = !hasSecurity || securityIsOptional\n return (\n
\n \n \n \n\n {!showSummary ? null :\n
\n {toString(resolvedSummary || summary)}\n
\n }\n\n {displayOperationId && (originalOperationId || operationId) ? {originalOperationId || operationId} : null}\n\n \n \n \n \n\n {\n allowAnonymous ? null :\n {\n const applicableDefinitions = authSelectors.definitionsForRequirements(security)\n authActions.showDefinitions(applicableDefinitions)\n }}\n />\n }\n {/* TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}\n
\n )\n\n }\n}\n","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { Iterable } from \"immutable\"\n\nexport default class OperationSummaryMethod extends PureComponent {\n\n static propTypes = {\n operationProps: PropTypes.instanceOf(Iterable).isRequired,\n method: PropTypes.string.isRequired,\n }\n\n static defaultProps = {\n operationProps: null,\n }\n render() {\n\n let {\n method,\n } = this.props\n\n return (\n {method.toUpperCase()}\n )\n }\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_splice_d746fc5e__[\"default\"] });","import React, { PureComponent } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { Iterable } from \"immutable\"\nimport { createDeepLinkPath } from \"core/utils\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class OperationSummaryPath extends PureComponent{\n\n static propTypes = {\n specPath: ImPropTypes.list.isRequired,\n operationProps: PropTypes.instanceOf(Iterable).isRequired,\n getComponent: PropTypes.func.isRequired,\n }\n\n render(){\n let {\n getComponent,\n operationProps,\n } = this.props\n\n\n let {\n deprecated,\n isShown,\n path,\n tag,\n operationId,\n isDeepLinkingEnabled,\n } = operationProps.toJS()\n\n /**\n * Add word-break elements between each segment, before the slash\n * to allow browsers an opportunity to break long paths into sensible segments.\n */\n const pathParts = path.split(/(?=\\/)/g)\n for (let i = 1; i < pathParts.length; i += 2) {\n pathParts.splice(i, 0, )\n }\n\n const DeepLink = getComponent( \"DeepLink\" )\n\n return(\n \n \n \n\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const OperationExt = ({ extensions, getComponent }) => {\n let OperationExtRow = getComponent(\"OperationExtRow\")\n return (\n
\n
\n

Extensions

\n
\n
\n\n \n \n \n \n \n \n \n \n {\n extensions.entrySeq().map(([k, v]) => )\n }\n \n
FieldValue
\n
\n
\n )\n}\nOperationExt.propTypes = {\n extensions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n}\n\nexport default OperationExt\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const OperationExtRow = ({ xKey, xVal }) => {\n const xNormalizedValue = !xVal ? null : xVal.toJS ? xVal.toJS() : xVal\n\n return (\n { xKey }\n { JSON.stringify(xNormalizedValue) }\n )\n}\nOperationExtRow.propTypes = {\n xKey: PropTypes.string,\n xVal: PropTypes.any\n}\n\nexport default OperationExtRow\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_js_file_download_bd23dbb6__[\"default\"] });","import React, { useRef, useEffect } from \"react\"\nimport PropTypes from \"prop-types\"\nimport cx from \"classnames\"\nimport {SyntaxHighlighter, getStyle} from \"core/syntax-highlighting\"\nimport get from \"lodash/get\"\nimport isFunction from \"lodash/isFunction\"\nimport saveAs from \"js-file-download\"\nimport { CopyToClipboard } from \"react-copy-to-clipboard\"\n\nconst HighlightCode = ({value, fileName, className, downloadable, getConfigs, canCopy, language}) => {\n const config = isFunction(getConfigs) ? getConfigs() : null\n const canSyntaxHighlight = get(config, \"syntaxHighlight\") !== false && get(config, \"syntaxHighlight.activated\", true)\n const rootRef = useRef(null)\n\n useEffect(() => {\n const childNodes = Array\n .from(rootRef.current.childNodes)\n .filter(node => !!node.nodeType && node.classList.contains(\"microlight\"))\n\n // eslint-disable-next-line no-use-before-define\n childNodes.forEach(node => node.addEventListener(\"mousewheel\", handlePreventYScrollingBeyondElement, { passive: false }))\n\n return () => {\n // eslint-disable-next-line no-use-before-define\n childNodes.forEach(node => node.removeEventListener(\"mousewheel\", handlePreventYScrollingBeyondElement))\n }\n }, [value, className, language])\n\n const handleDownload = () => {\n saveAs(value, fileName)\n }\n\n const handlePreventYScrollingBeyondElement = (e) => {\n const { target, deltaY } = e\n const { scrollHeight: contentHeight, offsetHeight: visibleHeight, scrollTop } = target\n const scrollOffset = visibleHeight + scrollTop\n const isElementScrollable = contentHeight > visibleHeight\n const isScrollingPastTop = scrollTop === 0 && deltaY < 0\n const isScrollingPastBottom = scrollOffset >= contentHeight && deltaY > 0\n\n if (isElementScrollable && (isScrollingPastTop || isScrollingPastBottom)) {\n e.preventDefault()\n }\n }\n\n return (\n
\n {!downloadable ? null :\n
\n Download\n
\n }\n\n {canCopy && (\n
\n
\n )}\n\n {canSyntaxHighlight\n ? \n {value}\n \n :
{value}
\n }\n\n
\n )\n}\n\nHighlightCode.propTypes = {\n value: PropTypes.string.isRequired,\n getConfigs: PropTypes.func.isRequired,\n className: PropTypes.string,\n downloadable: PropTypes.bool,\n fileName: PropTypes.string,\n language: PropTypes.string,\n canCopy: PropTypes.bool\n}\n\nHighlightCode.defaultProps = {\n fileName: \"response.txt\"\n}\n\nexport default HighlightCode\n","import React from \"react\"\nimport { fromJS, Iterable } from \"immutable\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { defaultStatusCode, getAcceptControllingResponse } from \"core/utils\"\nimport createHtmlReadyId from \"../../helpers/create-html-ready-id\"\n\nexport default class Responses extends React.Component {\n static propTypes = {\n tryItOutResponse: PropTypes.instanceOf(Iterable),\n responses: PropTypes.instanceOf(Iterable).isRequired,\n produces: PropTypes.instanceOf(Iterable),\n producesValue: PropTypes.any,\n displayRequestDuration: PropTypes.bool.isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n specPath: ImPropTypes.list.isRequired,\n fn: PropTypes.object.isRequired\n }\n\n static defaultProps = {\n tryItOutResponse: null,\n produces: fromJS([\"application/json\"]),\n displayRequestDuration: false\n }\n\n // These performance-enhancing checks were disabled as part of Multiple Examples\n // because they were causing data-consistency issues\n //\n // shouldComponentUpdate(nextProps) {\n // // BUG: props.tryItOutResponse is always coming back as a new Immutable instance\n // let render = this.props.tryItOutResponse !== nextProps.tryItOutResponse\n // || this.props.responses !== nextProps.responses\n // || this.props.produces !== nextProps.produces\n // || this.props.producesValue !== nextProps.producesValue\n // || this.props.displayRequestDuration !== nextProps.displayRequestDuration\n // || this.props.path !== nextProps.path\n // || this.props.method !== nextProps.method\n // return render\n // }\n\n\tonChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)\n\n onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {\n const { oas3Actions, path, method } = this.props\n if(controlsAcceptHeader) {\n oas3Actions.setResponseContentType({\n value,\n path,\n method\n })\n }\n }\n\n render() {\n let {\n responses,\n tryItOutResponse,\n getComponent,\n getConfigs,\n specSelectors,\n fn,\n producesValue,\n displayRequestDuration,\n specPath,\n path,\n method,\n oas3Selectors,\n oas3Actions,\n } = this.props\n let defaultCode = defaultStatusCode( responses )\n\n const ContentType = getComponent( \"contentType\" )\n const LiveResponse = getComponent( \"liveResponse\" )\n const Response = getComponent( \"response\" )\n\n let produces = this.props.produces && this.props.produces.size ? this.props.produces : Responses.defaultProps.produces\n\n const isSpecOAS3 = specSelectors.isOAS3()\n\n const acceptControllingResponse = isSpecOAS3 ?\n getAcceptControllingResponse(responses) : null\n\n const regionId = createHtmlReadyId(`${method}${path}_responses`)\n const controlId = `${regionId}_select`\n\n return (\n
\n
\n

Responses

\n { specSelectors.isOAS3() ? null : }\n
\n
\n {\n !tryItOutResponse ? null\n :
\n \n

Responses

\n
\n\n }\n\n \n \n \n \n \n { specSelectors.isOAS3() ? : null }\n \n \n \n {\n responses.entrySeq().map( ([code, response]) => {\n\n let className = tryItOutResponse && tryItOutResponse.get(\"status\") == code ? \"response_current\" : \"\"\n return (\n \n )\n }).toArray()\n }\n \n
CodeDescriptionLinks
\n
\n
\n )\n }\n}\n","/**\n * Replace invalid characters from a string to create an html-ready ID\n *\n * @param {string} id A string that may contain invalid characters for the HTML ID attribute\n * @param {string} [replacement=_] The string to replace invalid characters with; \"_\" by default\n * @return {string} Information about the parameter schema\n */\nexport default function createHtmlReadyId(id, replacement = \"_\") {\n return id.replace(/[^\\w-]/g, replacement)\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE__babel_runtime_corejs3_core_js_stable_instance_values_a68750d2__[\"default\"] });","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport cx from \"classnames\"\nimport { fromJS, Seq, Iterable, List, Map } from \"immutable\"\nimport { getExtensions, getSampleSchema, fromJSOrdered, stringify } from \"core/utils\"\nimport { getKnownSyntaxHighlighterLanguage } from \"core/utils/jsonParse\"\n\n\nconst getExampleComponent = ( sampleResponse, HighlightCode, getConfigs ) => {\n if (\n sampleResponse !== undefined &&\n sampleResponse !== null\n ) {\n let language = null\n let testValueForJson = getKnownSyntaxHighlighterLanguage(sampleResponse)\n if (testValueForJson) {\n language = \"json\"\n }\n return
\n \n
\n }\n return null\n}\n\nexport default class Response extends React.Component {\n constructor(props, context) {\n super(props, context)\n\n this.state = {\n responseContentType: \"\",\n }\n }\n\n static propTypes = {\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n code: PropTypes.string.isRequired,\n response: PropTypes.instanceOf(Iterable),\n className: PropTypes.string,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n specPath: ImPropTypes.list.isRequired,\n fn: PropTypes.object.isRequired,\n contentType: PropTypes.string,\n activeExamplesKey: PropTypes.string,\n controlsAcceptHeader: PropTypes.bool,\n onContentTypeChange: PropTypes.func\n }\n\n static defaultProps = {\n response: fromJS({}),\n onContentTypeChange: () => {}\n };\n\n _onContentTypeChange = (value) => {\n const { onContentTypeChange, controlsAcceptHeader } = this.props\n this.setState({ responseContentType: value })\n onContentTypeChange({\n value: value,\n controlsAcceptHeader\n })\n }\n\n getTargetExamplesKey = () => {\n const { response, contentType, activeExamplesKey } = this.props\n\n const activeContentType = this.state.responseContentType || contentType\n const activeMediaType = response.getIn([\"content\", activeContentType], Map({}))\n const examplesForMediaType = activeMediaType.get(\"examples\", null)\n\n const firstExamplesKey = examplesForMediaType.keySeq().first()\n return activeExamplesKey || firstExamplesKey\n }\n\n render() {\n let {\n path,\n method,\n code,\n response,\n className,\n specPath,\n fn,\n getComponent,\n getConfigs,\n specSelectors,\n contentType,\n controlsAcceptHeader,\n oas3Actions,\n } = this.props\n\n let { inferSchema } = fn\n let isOAS3 = specSelectors.isOAS3()\n const { showExtensions } = getConfigs()\n\n let extensions = showExtensions ? getExtensions(response) : null\n let headers = response.get(\"headers\")\n let links = response.get(\"links\")\n const ResponseExtension = getComponent(\"ResponseExtension\")\n const Headers = getComponent(\"headers\")\n const HighlightCode = getComponent(\"highlightCode\")\n const ModelExample = getComponent(\"modelExample\")\n const Markdown = getComponent(\"Markdown\", true)\n const OperationLink = getComponent(\"operationLink\")\n const ContentType = getComponent(\"contentType\")\n const ExamplesSelect = getComponent(\"ExamplesSelect\")\n const Example = getComponent(\"Example\")\n\n\n var schema, specPathWithPossibleSchema\n\n const activeContentType = this.state.responseContentType || contentType\n const activeMediaType = response.getIn([\"content\", activeContentType], Map({}))\n const examplesForMediaType = activeMediaType.get(\"examples\", null)\n\n // Goal: find a schema value for `schema`\n if(isOAS3) {\n const oas3SchemaForContentType = activeMediaType.get(\"schema\")\n\n schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null\n specPathWithPossibleSchema = oas3SchemaForContentType ? List([\"content\", this.state.responseContentType, \"schema\"]) : specPath\n } else {\n schema = response.get(\"schema\")\n specPathWithPossibleSchema = response.has(\"schema\") ? specPath.push(\"schema\") : specPath\n }\n\n let mediaTypeExample\n let shouldOverrideSchemaExample = false\n let sampleSchema\n let sampleGenConfig = {\n includeReadOnly: true\n }\n\n // Goal: find an example value for `sampleResponse`\n if(isOAS3) {\n sampleSchema = activeMediaType.get(\"schema\")?.toJS()\n if(examplesForMediaType) {\n const targetExamplesKey = this.getTargetExamplesKey()\n const targetExample = examplesForMediaType\n .get(targetExamplesKey, Map({}))\n const getMediaTypeExample = (targetExample) =>\n targetExample.get(\"value\")\n mediaTypeExample = getMediaTypeExample(targetExample)\n if(mediaTypeExample === undefined) {\n mediaTypeExample = getMediaTypeExample(examplesForMediaType.values().next().value)\n }\n shouldOverrideSchemaExample = true\n } else if(activeMediaType.get(\"example\") !== undefined) {\n // use the example key's value\n mediaTypeExample = activeMediaType.get(\"example\")\n shouldOverrideSchemaExample = true\n }\n } else {\n sampleSchema = schema\n sampleGenConfig = {...sampleGenConfig, includeWriteOnly: true}\n const oldOASMediaTypeExample = response.getIn([\"examples\", activeContentType])\n if(oldOASMediaTypeExample) {\n mediaTypeExample = oldOASMediaTypeExample\n shouldOverrideSchemaExample = true\n }\n }\n\n const sampleResponse = getSampleSchema(\n sampleSchema,\n activeContentType,\n sampleGenConfig,\n shouldOverrideSchemaExample ? mediaTypeExample : undefined\n )\n\n let example = getExampleComponent( sampleResponse, HighlightCode, getConfigs )\n\n return (\n \n \n { code }\n \n \n\n
\n \n
\n\n { !showExtensions || !extensions.size ? null : extensions.entrySeq().map(([key, v]) => )}\n\n {isOAS3 && response.get(\"content\") ? (\n
\n \n \n Media type\n \n \n {controlsAcceptHeader ? (\n \n Controls Accept header.\n \n ) : null}\n
\n {examplesForMediaType ? (\n
\n \n Examples\n \n \n oas3Actions.setActiveExamplesMember({\n name: key,\n pathMethod: [path, method],\n contextType: \"responses\",\n contextName: code\n })\n }\n showLabels={false}\n />\n
\n ) : null}\n \n ) : null}\n\n { example || schema ? (\n \n ) : null }\n\n { isOAS3 && examplesForMediaType ? (\n \n ) : null}\n\n { headers ? (\n \n ) : null}\n\n \n {isOAS3 ? \n { links ?\n links.toSeq().entrySeq().map(([key, link]) => {\n return \n })\n : No links}\n : null}\n \n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const ResponseExtension = ({ xKey, xVal }) => {\n return
{ xKey }: { String(xVal) }
\n}\nResponseExtension.propTypes = {\n xKey: PropTypes.string,\n xVal: PropTypes.any\n}\n\nexport default ResponseExtension\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_xml_but_prettier_2ed4d5cb__[\"default\"] });","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_lodash_toLower_c29ee2b0__[\"default\"] });","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport formatXml from \"xml-but-prettier\"\nimport toLower from \"lodash/toLower\"\nimport { extractFileNameFromContentDispositionHeader } from \"core/utils\"\nimport { getKnownSyntaxHighlighterLanguage } from \"core/utils/jsonParse\"\nimport win from \"core/window\"\n\nexport default class ResponseBody extends React.PureComponent {\n state = {\n parsedContent: null\n }\n\n static propTypes = {\n content: PropTypes.any.isRequired,\n contentType: PropTypes.string,\n getConfigs: PropTypes.func.isRequired,\n getComponent: PropTypes.func.isRequired,\n headers: PropTypes.object,\n url: PropTypes.string\n }\n\n updateParsedContent = (prevContent) => {\n const { content } = this.props\n\n if(prevContent === content) {\n return\n }\n\n if(content && content instanceof Blob) {\n var reader = new FileReader()\n reader.onload = () => {\n this.setState({\n parsedContent: reader.result\n })\n }\n reader.readAsText(content)\n } else {\n this.setState({\n parsedContent: content.toString()\n })\n }\n }\n\n componentDidMount() {\n this.updateParsedContent(null)\n }\n\n componentDidUpdate(prevProps) {\n this.updateParsedContent(prevProps.content)\n }\n\n render() {\n let { content, contentType, url, headers={}, getConfigs, getComponent } = this.props\n const { parsedContent } = this.state\n const HighlightCode = getComponent(\"highlightCode\")\n const downloadName = \"response_\" + new Date().getTime()\n let body, bodyEl\n url = url || \"\"\n\n if (\n /^application\\/octet-stream/i.test(contentType) ||\n (headers[\"Content-Disposition\"] && (/attachment/i).test(headers[\"Content-Disposition\"])) ||\n (headers[\"content-disposition\"] && (/attachment/i).test(headers[\"content-disposition\"])) ||\n (headers[\"Content-Description\"] && (/File Transfer/i).test(headers[\"Content-Description\"])) ||\n (headers[\"content-description\"] && (/File Transfer/i).test(headers[\"content-description\"]))) {\n // Download\n\n if (\"Blob\" in window) {\n let type = contentType || \"text/html\"\n let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})\n let href = window.URL.createObjectURL(blob)\n let fileName = url.substr(url.lastIndexOf(\"/\") + 1)\n let download = [type, fileName, href].join(\":\")\n\n // Use filename from response header,\n // First check if filename is quoted (e.g. contains space), if no, fallback to not quoted check\n let disposition = headers[\"content-disposition\"] || headers[\"Content-Disposition\"]\n if (typeof disposition !== \"undefined\") {\n let responseFilename = extractFileNameFromContentDispositionHeader(disposition)\n if (responseFilename !== null) {\n download = responseFilename\n }\n }\n\n if(win.navigator && win.navigator.msSaveOrOpenBlob) {\n bodyEl = \n } else {\n bodyEl = \n }\n } else {\n bodyEl =
Download headers detected but your browser does not support downloading binary via XHR (Blob).
\n }\n\n // Anything else (CORS)\n } else if (/json/i.test(contentType)) {\n // JSON\n let language = null\n let testValueForJson = getKnownSyntaxHighlighterLanguage(content)\n if (testValueForJson) {\n language = \"json\"\n }\n try {\n body = JSON.stringify(JSON.parse(content), null, \" \")\n } catch (error) {\n body = \"can't parse JSON. Raw result:\\n\\n\" + content\n }\n\n bodyEl = \n\n // XML\n } else if (/xml/i.test(contentType)) {\n body = formatXml(content, {\n textNodesOnSameLine: true,\n indentor: \" \"\n })\n bodyEl = \n\n // HTML or Plain Text\n } else if (toLower(contentType) === \"text/html\" || /text\\/plain/.test(contentType)) {\n bodyEl = \n\n // CSV\n } else if (toLower(contentType) === \"text/csv\" || /text\\/csv/.test(contentType)) {\n bodyEl = \n\n // Image\n } else if (/^image\\//i.test(contentType)) {\n if(contentType.includes(\"svg\")) {\n bodyEl =
{ content }
\n } else {\n bodyEl = \n }\n\n // Audio\n } else if (/^audio\\//i.test(contentType)) {\n bodyEl =
\n } else if (typeof content === \"string\") {\n bodyEl = \n } else if ( content.size > 0 ) {\n // We don't know the contentType, but there was some content returned\n if(parsedContent) {\n // We were able to squeeze something out of content\n // in `updateParsedContent`, so let's display it\n bodyEl =
\n

\n Unrecognized response type; displaying content as text.\n

\n \n
\n\n } else {\n // Give up\n bodyEl =

\n Unrecognized response type; unable to display.\n

\n }\n } else {\n // We don't know the contentType and there was no content returned\n bodyEl = null\n }\n\n return ( !bodyEl ? null :
\n
Response body
\n { bodyEl }\n
\n )\n }\n}\n","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { Map, List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class Parameters extends Component {\n\n constructor(props) {\n super(props)\n this.state = {\n callbackVisible: false,\n parametersVisible: true,\n }\n }\n\n static propTypes = {\n parameters: ImPropTypes.list.isRequired,\n operation: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n fn: PropTypes.object.isRequired,\n tryItOutEnabled: PropTypes.bool,\n allowTryItOut: PropTypes.bool,\n onTryoutClick: PropTypes.func,\n onCancelClick: PropTypes.func,\n onChangeKey: PropTypes.array,\n pathMethod: PropTypes.array.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specPath: ImPropTypes.list.isRequired,\n }\n\n\n static defaultProps = {\n onTryoutClick: Function.prototype,\n onCancelClick: Function.prototype,\n tryItOutEnabled: false,\n allowTryItOut: true,\n onChangeKey: [],\n specPath: [],\n }\n\n onChange = (param, value, isXml) => {\n let {\n specActions: { changeParamByIdentity },\n onChangeKey,\n } = this.props\n\n changeParamByIdentity(onChangeKey, param, value, isXml)\n }\n\n onChangeConsumesWrapper = (val) => {\n let {\n specActions: { changeConsumesValue },\n onChangeKey,\n } = this.props\n\n changeConsumesValue(onChangeKey, val)\n }\n\n toggleTab = (tab) => {\n if (tab === \"parameters\") {\n return this.setState({\n parametersVisible: true,\n callbackVisible: false,\n })\n } else if (tab === \"callbacks\") {\n return this.setState({\n callbackVisible: true,\n parametersVisible: false,\n })\n }\n }\n\n onChangeMediaType = ({ value, pathMethod }) => {\n let { specActions, oas3Selectors, oas3Actions } = this.props\n const userHasEditedBody = oas3Selectors.hasUserEditedBody(...pathMethod)\n const shouldRetainRequestBodyValue = oas3Selectors.shouldRetainRequestBodyValue(...pathMethod)\n oas3Actions.setRequestContentType({ value, pathMethod })\n oas3Actions.initRequestBodyValidateError({ pathMethod })\n if (!userHasEditedBody) {\n if(!shouldRetainRequestBodyValue) {\n oas3Actions.setRequestBodyValue({ value: undefined, pathMethod })\n }\n specActions.clearResponse(...pathMethod)\n specActions.clearRequest(...pathMethod)\n specActions.clearValidateParams(pathMethod)\n }\n }\n\n render() {\n\n let {\n onTryoutClick,\n parameters,\n allowTryItOut,\n tryItOutEnabled,\n specPath,\n fn,\n getComponent,\n getConfigs,\n specSelectors,\n specActions,\n pathMethod,\n oas3Actions,\n oas3Selectors,\n operation,\n } = this.props\n\n const ParameterRow = getComponent(\"parameterRow\")\n const TryItOutButton = getComponent(\"TryItOutButton\")\n const ContentType = getComponent(\"contentType\")\n const Callbacks = getComponent(\"Callbacks\", true)\n const RequestBody = getComponent(\"RequestBody\", true)\n\n const isExecute = tryItOutEnabled && allowTryItOut\n const isOAS3 = specSelectors.isOAS3()\n\n\n const requestBody = operation.get(\"requestBody\")\n\n const groupedParametersArr = Object.values(parameters\n .reduce((acc, x) => {\n const key = x.get(\"in\")\n acc[key] ??= []\n acc[key].push(x)\n return acc\n }, {}))\n .reduce((acc, x) => acc.concat(x), [])\n\n const retainRequestBodyValueFlagForOperation = (f) => oas3Actions.setRetainRequestBodyValueFlag({ value: f, pathMethod })\n return (\n
\n
\n {isOAS3 ? (\n
\n
this.toggleTab(\"parameters\")}\n className={`tab-item ${this.state.parametersVisible && \"active\"}`}>\n

Parameters

\n
\n {operation.get(\"callbacks\") ?\n (\n
this.toggleTab(\"callbacks\")}\n className={`tab-item ${this.state.callbackVisible && \"active\"}`}>\n

Callbacks

\n
\n ) : null\n }\n
\n ) : (\n
\n

Parameters

\n
\n )}\n {allowTryItOut ? (\n oas3Actions.setRequestBodyValue({ value: undefined, pathMethod })}/>\n ) : null}\n
\n {this.state.parametersVisible ?
\n {!groupedParametersArr.length ?

No parameters

:\n
\n \n \n \n \n \n \n \n \n {\n groupedParametersArr.map((parameter, i) => (\n \n ))\n }\n \n
NameDescription
\n
\n }\n
: null}\n\n {this.state.callbackVisible ?
\n \n
: null}\n {\n isOAS3 && requestBody && this.state.parametersVisible &&\n
\n
\n

Request\n body

\n \n
\n
\n {\n this.props.oas3Actions.setActiveExamplesMember({\n name: key,\n pathMethod: this.props.pathMethod,\n contextType: \"requestBody\",\n contextName: \"requestBody\", // RBs are currently not stored per-mediaType\n })\n }\n }\n onChange={(value, path) => {\n if (path) {\n const lastValue = oas3Selectors.requestBodyValue(...pathMethod)\n const usableValue = Map.isMap(lastValue) ? lastValue : Map()\n return oas3Actions.setRequestBodyValue({\n pathMethod,\n value: usableValue.setIn(path, value),\n })\n }\n oas3Actions.setRequestBodyValue({ value, pathMethod })\n }}\n onChangeIncludeEmpty={(name, value) => {\n oas3Actions.setRequestBodyInclusion({\n pathMethod,\n value,\n name,\n })\n }}\n contentType={oas3Selectors.requestContentType(...pathMethod)} />\n
\n
\n }\n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const ParameterExt = ({ xKey, xVal }) => {\n return
{ xKey }: { String(xVal) }
\n}\nParameterExt.propTypes = {\n xKey: PropTypes.string,\n xVal: PropTypes.any\n}\n\nexport default ParameterExt\n","import React, { Component } from \"react\"\nimport cx from \"classnames\"\nimport PropTypes from \"prop-types\"\n\n\nconst noop = () => { }\n\nconst ParameterIncludeEmptyPropTypes = {\n isIncluded: PropTypes.bool.isRequired,\n isDisabled: PropTypes.bool.isRequired,\n isIncludedOptions: PropTypes.object,\n onChange: PropTypes.func.isRequired,\n}\n\nconst ParameterIncludeEmptyDefaultProps = {\n onChange: noop,\n isIncludedOptions: {},\n}\nexport default class ParameterIncludeEmpty extends Component {\n static propTypes = ParameterIncludeEmptyPropTypes\n static defaultProps = ParameterIncludeEmptyDefaultProps\n\n componentDidMount() {\n const { isIncludedOptions, onChange } = this.props\n const { shouldDispatchInit, defaultValue } = isIncludedOptions\n if (shouldDispatchInit) {\n onChange(defaultValue)\n }\n }\n\n onCheckboxChange = e => {\n const { onChange } = this.props\n onChange(e.target.checked)\n }\n\n render() {\n let { isIncluded, isDisabled } = this.props\n\n return (\n
\n \n
\n )\n }\n}\n","import React, { Component } from \"react\"\nimport { Map, List } from \"immutable\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport win from \"core/window\"\nimport { getSampleSchema, getExtensions, getCommonExtensions, numberToString, stringify, isEmptyValue } from \"core/utils\"\nimport getParameterSchema from \"../../helpers/get-parameter-schema.js\"\n\nexport default class ParameterRow extends Component {\n static propTypes = {\n onChange: PropTypes.func.isRequired,\n param: PropTypes.object.isRequired,\n rawParam: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n fn: PropTypes.object.isRequired,\n isExecute: PropTypes.bool,\n onChangeConsumes: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n pathMethod: PropTypes.array.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specPath: ImPropTypes.list.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n }\n\n constructor(props, context) {\n super(props, context)\n\n this.setDefaultValue()\n }\n\n UNSAFE_componentWillReceiveProps(props) {\n let { specSelectors, pathMethod, rawParam } = props\n let isOAS3 = specSelectors.isOAS3()\n\n let parameterWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || new Map()\n // fallback, if the meta lookup fails\n parameterWithMeta = parameterWithMeta.isEmpty() ? rawParam : parameterWithMeta\n\n let enumValue\n\n if(isOAS3) {\n let { schema } = getParameterSchema(parameterWithMeta, { isOAS3 })\n enumValue = schema ? schema.get(\"enum\") : undefined\n } else {\n enumValue = parameterWithMeta ? parameterWithMeta.get(\"enum\") : undefined\n }\n let paramValue = parameterWithMeta ? parameterWithMeta.get(\"value\") : undefined\n\n let value\n\n if ( paramValue !== undefined ) {\n value = paramValue\n } else if ( rawParam.get(\"required\") && enumValue && enumValue.size ) {\n value = enumValue.first()\n }\n\n if ( value !== undefined && value !== paramValue ) {\n this.onChangeWrapper(numberToString(value))\n }\n // todo: could check if schema here; if not, do not call. impact?\n this.setDefaultValue()\n }\n\n onChangeWrapper = (value, isXml = false) => {\n let { onChange, rawParam } = this.props\n let valueForUpstream\n\n // Coerce empty strings and empty Immutable objects to null\n if(value === \"\" || (value && value.size === 0)) {\n valueForUpstream = null\n } else {\n valueForUpstream = value\n }\n\n return onChange(rawParam, valueForUpstream, isXml)\n }\n\n _onExampleSelect = (key, /* { isSyntheticChange } = {} */) => {\n this.props.oas3Actions.setActiveExamplesMember({\n name: key,\n pathMethod: this.props.pathMethod,\n contextType: \"parameters\",\n contextName: this.getParamKey()\n })\n }\n\n onChangeIncludeEmpty = (newValue) => {\n let { specActions, param, pathMethod } = this.props\n const paramName = param.get(\"name\")\n const paramIn = param.get(\"in\")\n return specActions.updateEmptyParamInclusion(pathMethod, paramName, paramIn, newValue)\n }\n\n setDefaultValue = () => {\n let { specSelectors, pathMethod, rawParam, oas3Selectors } = this.props\n\n const paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()\n const { schema } = getParameterSchema(paramWithMeta, { isOAS3: specSelectors.isOAS3() })\n const parameterMediaType = paramWithMeta\n .get(\"content\", Map())\n .keySeq()\n .first()\n\n // getSampleSchema could return null\n const generatedSampleValue = schema ? getSampleSchema(schema.toJS(), parameterMediaType, {\n\n includeWriteOnly: true\n }) : null\n\n if (!paramWithMeta || paramWithMeta.get(\"value\") !== undefined) {\n return\n }\n\n if( paramWithMeta.get(\"in\") !== \"body\" ) {\n let initialValue\n\n //// Find an initial value\n\n if (specSelectors.isSwagger2()) {\n initialValue =\n paramWithMeta.get(\"x-example\") !== undefined\n ? paramWithMeta.get(\"x-example\")\n : paramWithMeta.getIn([\"schema\", \"example\"]) !== undefined\n ? paramWithMeta.getIn([\"schema\", \"example\"])\n : (schema && schema.getIn([\"default\"]))\n } else if (specSelectors.isOAS3()) {\n const currentExampleKey = oas3Selectors.activeExamplesMember(...pathMethod, \"parameters\", this.getParamKey())\n initialValue =\n paramWithMeta.getIn([\"examples\", currentExampleKey, \"value\"]) !== undefined\n ? paramWithMeta.getIn([\"examples\", currentExampleKey, \"value\"])\n : paramWithMeta.getIn([\"content\", parameterMediaType, \"example\"]) !== undefined\n ? paramWithMeta.getIn([\"content\", parameterMediaType, \"example\"])\n : paramWithMeta.get(\"example\") !== undefined\n ? paramWithMeta.get(\"example\")\n : (schema && schema.get(\"example\")) !== undefined\n ? (schema && schema.get(\"example\"))\n : (schema && schema.get(\"default\")) !== undefined\n ? (schema && schema.get(\"default\"))\n : paramWithMeta.get(\"default\") // ensures support for `parameterMacro`\n }\n\n //// Process the initial value\n\n if(initialValue !== undefined && !List.isList(initialValue)) {\n // Stringify if it isn't a List\n initialValue = stringify(initialValue)\n }\n\n //// Dispatch the initial value\n\n if(initialValue !== undefined) {\n this.onChangeWrapper(initialValue)\n } else if(\n schema && schema.get(\"type\") === \"object\"\n && generatedSampleValue\n && !paramWithMeta.get(\"examples\")\n ) {\n // Object parameters get special treatment.. if the user doesn't set any\n // default or example values, we'll provide initial values generated from\n // the schema.\n // However, if `examples` exist for the parameter, we won't do anything,\n // so that the appropriate `examples` logic can take over.\n this.onChangeWrapper(\n List.isList(generatedSampleValue) ? (\n generatedSampleValue\n ) : (\n stringify(generatedSampleValue)\n )\n )\n }\n }\n }\n\n getParamKey() {\n const { param } = this.props\n\n if(!param) return null\n\n return `${param.get(\"name\")}-${param.get(\"in\")}`\n }\n\n render() {\n let {param, rawParam, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath, oas3Selectors} = this.props\n\n let isOAS3 = specSelectors.isOAS3()\n\n const { showExtensions, showCommonExtensions } = getConfigs()\n\n if(!param) {\n param = rawParam\n }\n\n if(!rawParam) return null\n\n // const onChangeWrapper = (value) => onChange(param, value)\n const JsonSchemaForm = getComponent(\"JsonSchemaForm\")\n const ParamBody = getComponent(\"ParamBody\")\n let inType = param.get(\"in\")\n let bodyParam = inType !== \"body\" ? null\n : \n\n const ModelExample = getComponent(\"modelExample\")\n const Markdown = getComponent(\"Markdown\", true)\n const ParameterExt = getComponent(\"ParameterExt\")\n const ParameterIncludeEmpty = getComponent(\"ParameterIncludeEmpty\")\n const ExamplesSelectValueRetainer = getComponent(\"ExamplesSelectValueRetainer\")\n const Example = getComponent(\"Example\")\n\n let { schema } = getParameterSchema(param, { isOAS3 })\n let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()\n\n let format = schema ? schema.get(\"format\") : null\n let type = schema ? schema.get(\"type\") : null\n let itemType = schema ? schema.getIn([\"items\", \"type\"]) : null\n let isFormData = inType === \"formData\"\n let isFormDataSupported = \"FormData\" in win\n let required = param.get(\"required\")\n\n let value = paramWithMeta ? paramWithMeta.get(\"value\") : \"\"\n let commonExt = showCommonExtensions ? getCommonExtensions(schema) : null\n let extensions = showExtensions ? getExtensions(param) : null\n\n let paramItems // undefined\n let paramEnum // undefined\n let paramDefaultValue // undefined\n let paramExample // undefined\n let isDisplayParamEnum = false\n\n if ( param !== undefined && schema ) {\n paramItems = schema.get(\"items\")\n }\n\n if (paramItems !== undefined) {\n paramEnum = paramItems.get(\"enum\")\n paramDefaultValue = paramItems.get(\"default\")\n } else if (schema) {\n paramEnum = schema.get(\"enum\")\n }\n\n if ( paramEnum && paramEnum.size && paramEnum.size > 0) {\n isDisplayParamEnum = true\n }\n\n // Default and Example Value for readonly doc\n if ( param !== undefined ) {\n if (schema) {\n paramDefaultValue = schema.get(\"default\")\n }\n if (paramDefaultValue === undefined) {\n paramDefaultValue = param.get(\"default\")\n }\n paramExample = param.get(\"example\")\n if (paramExample === undefined) {\n paramExample = param.get(\"x-example\")\n }\n }\n\n return (\n \n \n
\n { param.get(\"name\") }\n { !required ? null :  * }\n
\n
\n { type }\n { itemType && `[${itemType}]` }\n { format && (${format})}\n
\n
\n { isOAS3 && param.get(\"deprecated\") ? \"deprecated\": null }\n
\n
({ param.get(\"in\") })
\n { !showCommonExtensions || !commonExt.size ? null : commonExt.entrySeq().map(([key, v]) => )}\n { !showExtensions || !extensions.size ? null : extensions.entrySeq().map(([key, v]) => )}\n \n\n \n { param.get(\"description\") ? : null }\n\n { (bodyParam || !isExecute) && isDisplayParamEnum ?\n Available values
: \" + paramEnum.map(function(item) {\n return item\n }).toArray().join(\", \")}/>\n : null\n }\n\n { (bodyParam || !isExecute) && paramDefaultValue !== undefined ?\n Default value : \" + paramDefaultValue}/>\n : null\n }\n\n { (bodyParam || !isExecute) && paramExample !== undefined ?\n Example : \" + paramExample}/>\n : null\n }\n\n {(isFormData && !isFormDataSupported) &&
Error: your browser does not support FormData
}\n\n {\n isOAS3 && param.get(\"examples\") ? (\n
\n \n
\n ) : null\n }\n\n { bodyParam ? null\n : \n }\n\n\n {\n bodyParam && schema ? \n : null\n }\n\n {\n !bodyParam && isExecute && param.get(\"allowEmptyValue\") ?\n \n : null\n }\n\n {\n isOAS3 && param.get(\"examples\") ? (\n \n ) : null\n }\n\n \n\n \n )\n\n }\n\n}\n","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class Execute extends Component {\n\n static propTypes = {\n specSelectors: PropTypes.object.isRequired,\n specActions: PropTypes.object.isRequired,\n operation: PropTypes.object.isRequired,\n path: PropTypes.string.isRequired,\n method: PropTypes.string.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n onExecute: PropTypes.func,\n disabled: PropTypes.bool\n }\n\n handleValidateParameters = () => {\n let { specSelectors, specActions, path, method } = this.props\n specActions.validateParams([path, method])\n return specSelectors.validateBeforeExecute([path, method])\n }\n\n handleValidateRequestBody = () => {\n let { path, method, specSelectors, oas3Selectors, oas3Actions } = this.props\n let validationErrors = {\n missingBodyValue: false,\n missingRequiredKeys: []\n }\n // context: reset errors, then (re)validate\n oas3Actions.clearRequestBodyValidateError({ path, method })\n let oas3RequiredRequestBodyContentType = specSelectors.getOAS3RequiredRequestBodyContentType([path, method])\n let oas3RequestBodyValue = oas3Selectors.requestBodyValue(path, method)\n let oas3ValidateBeforeExecuteSuccess = oas3Selectors.validateBeforeExecute([path, method])\n let oas3RequestContentType = oas3Selectors.requestContentType(path, method)\n\n if (!oas3ValidateBeforeExecuteSuccess) {\n validationErrors.missingBodyValue = true\n oas3Actions.setRequestBodyValidateError({ path, method, validationErrors })\n return false\n }\n if (!oas3RequiredRequestBodyContentType) {\n return true\n }\n let missingRequiredKeys = oas3Selectors.validateShallowRequired({\n oas3RequiredRequestBodyContentType,\n oas3RequestContentType,\n oas3RequestBodyValue\n })\n if (!missingRequiredKeys || missingRequiredKeys.length < 1) {\n return true\n }\n missingRequiredKeys.forEach((missingKey) => {\n validationErrors.missingRequiredKeys.push(missingKey)\n })\n oas3Actions.setRequestBodyValidateError({ path, method, validationErrors })\n return false\n }\n\n handleValidationResultPass = () => {\n let { specActions, operation, path, method } = this.props\n if (this.props.onExecute) {\n // loading spinner\n this.props.onExecute()\n }\n specActions.execute({ operation, path, method })\n }\n\n handleValidationResultFail = () => {\n let { specActions, path, method } = this.props\n // deferred by 40ms, to give element class change time to settle.\n specActions.clearValidateParams([path, method])\n setTimeout(() => {\n specActions.validateParams([path, method])\n }, 40)\n }\n\n handleValidationResult = (isPass) => {\n if (isPass) {\n this.handleValidationResultPass()\n } else {\n this.handleValidationResultFail()\n }\n }\n\n onClick = () => {\n let paramsResult = this.handleValidateParameters()\n let requestBodyResult = this.handleValidateRequestBody()\n let isPass = paramsResult && requestBodyResult\n this.handleValidationResult(isPass)\n }\n\n onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)\n\n render(){\n const { disabled } = this.props\n return (\n \n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport Im from \"immutable\"\n\nconst propClass = \"header-example\"\n\nexport default class Headers extends React.Component {\n static propTypes = {\n headers: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n };\n\n render() {\n let { headers, getComponent } = this.props\n\n const Property = getComponent(\"Property\")\n const Markdown = getComponent(\"Markdown\", true)\n\n if ( !headers || !headers.size )\n return null\n\n return (\n
\n

Headers:

\n \n \n \n \n \n \n \n \n \n {\n headers.entrySeq().map( ([ key, header ]) => {\n if(!Im.Map.isMap(header)) {\n return null\n }\n\n const description = header.get(\"description\")\n const type = header.getIn([\"schema\"]) ? header.getIn([\"schema\", \"type\"]) : header.getIn([\"type\"])\n const schemaExample = header.getIn([\"schema\", \"example\"])\n\n return (\n \n \n \n )\n }).toArray()\n }\n \n
NameDescriptionType
{ key }{\n !description ? null : \n }{ type } { schemaExample ? : null }
\n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List } from \"immutable\"\n\nexport default class Errors extends React.Component {\n\n static propTypes = {\n editorActions: PropTypes.object,\n errSelectors: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object.isRequired,\n layoutActions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n }\n\n render() {\n let { editorActions, errSelectors, layoutSelectors, layoutActions, getComponent } = this.props\n\n const Collapse = getComponent(\"Collapse\")\n\n if(editorActions && editorActions.jumpToLine) {\n var jumpToLine = editorActions.jumpToLine\n }\n\n let errors = errSelectors.allErrors()\n\n // all thrown errors, plus error-level everything else\n let allErrorsToDisplay = errors.filter(err => err.get(\"type\") === \"thrown\" ? true :err.get(\"level\") === \"error\")\n\n if(!allErrorsToDisplay || allErrorsToDisplay.count() < 1) {\n return null\n }\n\n let isVisible = layoutSelectors.isShown([\"errorPane\"], true)\n let toggleVisibility = () => layoutActions.show([\"errorPane\"], !isVisible)\n\n let sortedJSErrors = allErrorsToDisplay.sortBy(err => err.get(\"line\"))\n\n return (\n
\n        
\n

Errors

\n \n
\n \n
\n { sortedJSErrors.map((err, i) => {\n let type = err.get(\"type\")\n if(type === \"thrown\" || type === \"auth\") {\n return \n }\n if(type === \"spec\") {\n return \n }\n }) }\n
\n
\n
\n )\n }\n}\n\nconst ThrownErrorItem = ( { error, jumpToLine } ) => {\n if(!error) {\n return null\n }\n let errorLine = error.get(\"line\")\n\n return (\n
\n { !error ? null :\n
\n

{ (error.get(\"source\") && error.get(\"level\")) ?\n toTitleCase(error.get(\"source\")) + \" \" + error.get(\"level\") : \"\" }\n { error.get(\"path\") ? at {error.get(\"path\")}: null }

\n \n { error.get(\"message\") }\n \n
\n { errorLine && jumpToLine ? Jump to line { errorLine } : null }\n
\n
\n }\n
\n )\n }\n\nconst SpecErrorItem = ( { error, jumpToLine } ) => {\n let locationMessage = null\n\n if(error.get(\"path\")) {\n if(List.isList(error.get(\"path\"))) {\n locationMessage = at { error.get(\"path\").join(\".\") }\n } else {\n locationMessage = at { error.get(\"path\") }\n }\n } else if(error.get(\"line\") && !jumpToLine) {\n locationMessage = on line { error.get(\"line\") }\n }\n\n return (\n
\n { !error ? null :\n
\n

{ toTitleCase(error.get(\"source\")) + \" \" + error.get(\"level\") } { locationMessage }

\n { error.get(\"message\") }\n
\n { jumpToLine ? (\n Jump to line { error.get(\"line\") }\n ) : null }\n
\n
\n }\n
\n )\n }\n\nfunction toTitleCase(str) {\n return (str || \"\")\n .split(\" \")\n .map(substr => substr[0].toUpperCase() + substr.slice(1))\n .join(\" \")\n}\n\nThrownErrorItem.propTypes = {\n error: PropTypes.object.isRequired,\n jumpToLine: PropTypes.func\n}\n\nThrownErrorItem.defaultProps = {\n jumpToLine: null\n}\n\nSpecErrorItem.propTypes = {\n error: PropTypes.object.isRequired,\n jumpToLine: PropTypes.func\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport { fromJS } from \"immutable\"\n\nconst noop = ()=>{}\n\nexport default class ContentType extends React.Component {\n\n static propTypes = {\n ariaControls: PropTypes.string,\n contentTypes: PropTypes.oneOfType([ImPropTypes.list, ImPropTypes.set, ImPropTypes.seq]),\n controlId: PropTypes.string,\n value: PropTypes.string,\n onChange: PropTypes.func,\n className: PropTypes.string,\n ariaLabel: PropTypes.string\n }\n\n static defaultProps = {\n onChange: noop,\n value: null,\n contentTypes: fromJS([\"application/json\"]),\n }\n\n componentDidMount() {\n // Needed to populate the form, initially\n if(this.props.contentTypes) {\n this.props.onChange(this.props.contentTypes.first())\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n if(!nextProps.contentTypes || !nextProps.contentTypes.size) {\n return\n }\n\n if(!nextProps.contentTypes.includes(nextProps.value)) {\n nextProps.onChange(nextProps.contentTypes.first())\n }\n }\n\n onChangeWrapper = e => this.props.onChange(e.target.value)\n\n render() {\n let { ariaControls, ariaLabel, className, contentTypes, controlId, value } = this.props\n\n if ( !contentTypes || !contentTypes.size )\n return null\n\n return (\n
\n \n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nfunction xclass(...args) {\n return args.filter(a => !!a).join(\" \").trim()\n}\n\nexport class Container extends React.Component {\n render() {\n let { fullscreen, full, ...rest } = this.props\n // Normal element\n\n if(fullscreen)\n return
\n\n let containerClass = \"swagger-container\" + (full ? \"-full\" : \"\")\n return (\n
\n )\n }\n}\n\nContainer.propTypes = {\n fullscreen: PropTypes.bool,\n full: PropTypes.bool,\n className: PropTypes.string\n}\n\nconst DEVICES = {\n \"mobile\": \"\",\n \"tablet\": \"-tablet\",\n \"desktop\": \"-desktop\",\n \"large\": \"-hd\"\n}\n\nexport class Col extends React.Component {\n\n render() {\n const {\n hide,\n keepContents,\n /* we don't want these in the `rest` object that passes to the final component,\n since React now complains. So we extract them */\n /* eslint-disable no-unused-vars */\n mobile,\n tablet,\n desktop,\n large,\n /* eslint-enable no-unused-vars */\n ...rest\n } = this.props\n\n if(hide && !keepContents)\n return \n\n let classesAr = []\n\n for (let device in DEVICES) {\n if (!Object.prototype.hasOwnProperty.call(DEVICES, device)) {\n continue\n }\n let deviceClass = DEVICES[device]\n if(device in this.props) {\n let val = this.props[device]\n\n if(val < 1) {\n classesAr.push(\"none\" + deviceClass)\n continue\n }\n\n classesAr.push(\"block\" + deviceClass)\n classesAr.push(\"col-\" + val + deviceClass)\n }\n }\n\n if (hide) {\n classesAr.push(\"hidden\")\n }\n\n let classes = xclass(rest.className, ...classesAr)\n\n return (\n
\n )\n }\n\n}\n\nCol.propTypes = {\n hide: PropTypes.bool,\n keepContents: PropTypes.bool,\n mobile: PropTypes.number,\n tablet: PropTypes.number,\n desktop: PropTypes.number,\n large: PropTypes.number,\n className: PropTypes.string\n}\n\nexport class Row extends React.Component {\n\n render() {\n return
\n }\n\n}\n\nRow.propTypes = {\n className: PropTypes.string\n}\n\nexport class Button extends React.Component {\n\n static propTypes = {\n className: PropTypes.string\n }\n\n static defaultProps = {\n className: \"\"\n }\n\n render() {\n return
\n
\n {curlBlock}\n
\n \n )\n }\n\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class Schemes extends React.Component {\n\n static propTypes = {\n specActions: PropTypes.object.isRequired,\n schemes: PropTypes.object.isRequired,\n currentScheme: PropTypes.string.isRequired,\n path: PropTypes.string,\n method: PropTypes.string,\n }\n\n UNSAFE_componentWillMount() {\n let { schemes } = this.props\n\n //fire 'change' event to set default 'value' of select\n this.setScheme(schemes.first())\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n if ( !this.props.currentScheme || !nextProps.schemes.includes(this.props.currentScheme) ) {\n // if we don't have a selected currentScheme or if our selected scheme is no longer an option,\n // then fire 'change' event and select the first scheme in the list of options\n this.setScheme(nextProps.schemes.first())\n }\n }\n\n onChange =( e ) => {\n this.setScheme( e.target.value )\n }\n\n setScheme = ( value ) => {\n let { path, method, specActions } = this.props\n\n specActions.setScheme( value, path, method )\n }\n\n render() {\n let { schemes, currentScheme } = this.props\n\n return (\n \n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class SchemesContainer extends React.Component {\n\n static propTypes = {\n specActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n }\n\n render () {\n const {specActions, specSelectors, getComponent} = this.props\n\n const currentScheme = specSelectors.operationScheme()\n const schemes = specSelectors.schemes()\n\n const Schemes = getComponent(\"schemes\")\n\n const schemesArePresent = schemes && schemes.size\n\n return schemesArePresent ? (\n \n ) : null\n }\n}\n","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport Im from \"immutable\"\n\nexport default class ModelCollapse extends Component {\n static propTypes = {\n collapsedContent: PropTypes.any,\n expanded: PropTypes.bool,\n children: PropTypes.any,\n title: PropTypes.element,\n modelName: PropTypes.string,\n classes: PropTypes.string,\n onToggle: PropTypes.func,\n hideSelfOnExpand: PropTypes.bool,\n layoutActions: PropTypes.object,\n layoutSelectors: PropTypes.object.isRequired,\n specPath: ImPropTypes.list.isRequired,\n }\n\n static defaultProps = {\n collapsedContent: \"{...}\",\n expanded: false,\n title: null,\n onToggle: () => {},\n hideSelfOnExpand: false,\n specPath: Im.List([]),\n }\n\n constructor(props, context) {\n super(props, context)\n\n let { expanded, collapsedContent } = this.props\n\n this.state = {\n expanded : expanded,\n collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent\n }\n }\n\n componentDidMount() {\n const { hideSelfOnExpand, expanded, modelName } = this.props\n if(hideSelfOnExpand && expanded) {\n // We just mounted pre-expanded, and we won't be going back..\n // So let's give our parent an `onToggle` call..\n // Since otherwise it will never be called.\n this.props.onToggle(modelName, expanded)\n }\n }\n\n UNSAFE_componentWillReceiveProps(nextProps){\n if(this.props.expanded !== nextProps.expanded){\n this.setState({expanded: nextProps.expanded})\n }\n }\n\n toggleCollapsed=()=>{\n if(this.props.onToggle){\n this.props.onToggle(this.props.modelName,!this.state.expanded)\n }\n\n this.setState({\n expanded: !this.state.expanded\n })\n }\n\n onLoad = (ref) => {\n if (ref && this.props.layoutSelectors) {\n const scrollToKey = this.props.layoutSelectors.getScrollToKey()\n\n if( Im.is(scrollToKey, this.props.specPath) ) this.toggleCollapsed()\n this.props.layoutActions.readyToScroll(this.props.specPath, ref.parentElement)\n }\n }\n\n render () {\n const { title, classes } = this.props\n\n if(this.state.expanded ) {\n if(this.props.hideSelfOnExpand) {\n return \n {this.props.children}\n \n }\n }\n\n return (\n \n \n\n { this.state.expanded && this.props.children }\n \n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport cx from \"classnames\"\nimport randomBytes from \"randombytes\"\n\nexport default class ModelExample extends React.Component {\n static propTypes = {\n getComponent: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n schema: PropTypes.object.isRequired,\n example: PropTypes.any.isRequired,\n isExecute: PropTypes.bool,\n getConfigs: PropTypes.func.isRequired,\n specPath: ImPropTypes.list.isRequired,\n includeReadOnly: PropTypes.bool,\n includeWriteOnly: PropTypes.bool,\n }\n\n constructor(props, context) {\n super(props, context)\n let { getConfigs, isExecute } = this.props\n let { defaultModelRendering } = getConfigs()\n\n let activeTab = defaultModelRendering\n\n if (defaultModelRendering !== \"example\" && defaultModelRendering !== \"model\") {\n activeTab = \"example\"\n }\n\n if(isExecute) {\n activeTab = \"example\"\n }\n\n this.state = {\n activeTab,\n }\n }\n\n activeTab = ( e ) => {\n let { target : { dataset : { name } } } = e\n\n this.setState({\n activeTab: name\n })\n }\n\n UNSAFE_componentWillReceiveProps(nextProps) {\n if (\n nextProps.isExecute &&\n !this.props.isExecute &&\n this.props.example\n ) {\n this.setState({ activeTab: \"example\" })\n }\n }\n\n render() {\n let { getComponent, specSelectors, schema, example, isExecute, getConfigs, specPath, includeReadOnly, includeWriteOnly } = this.props\n let { defaultModelExpandDepth } = getConfigs()\n const ModelWrapper = getComponent(\"ModelWrapper\")\n const HighlightCode = getComponent(\"highlightCode\")\n const exampleTabId = randomBytes(5).toString(\"base64\")\n const examplePanelId = randomBytes(5).toString(\"base64\")\n const modelTabId = randomBytes(5).toString(\"base64\")\n const modelPanelId = randomBytes(5).toString(\"base64\")\n\n let isOAS3 = specSelectors.isOAS3()\n\n return (\n
\n
    \n
  • \n \n {isExecute ? \"Edit Value\" : \"Example Value\"}\n \n
  • \n { schema && (\n
  • \n \n {isOAS3 ? \"Schema\" : \"Model\" }\n \n
  • \n )}\n
\n {this.state.activeTab === \"example\" && (\n \n {example ? example : (\n \n )}\n
\n )}\n\n {this.state.activeTab === \"model\" && (\n \n \n \n )}\n \n )\n }\n\n}\n","import React, { Component, } from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nexport default class ModelWrapper extends Component {\n\n static propTypes = {\n schema: PropTypes.object.isRequired,\n name: PropTypes.string,\n displayName: PropTypes.string,\n fullPath: PropTypes.array.isRequired,\n specPath: ImPropTypes.list.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n expandDepth: PropTypes.number,\n layoutActions: PropTypes.object,\n layoutSelectors: PropTypes.object.isRequired,\n includeReadOnly: PropTypes.bool,\n includeWriteOnly: PropTypes.bool,\n }\n\n onToggle = (name,isShown) => {\n // If this prop is present, we'll have deepLinking for it\n if(this.props.layoutActions) {\n this.props.layoutActions.show(this.props.fullPath, isShown)\n }\n }\n\n render(){\n let { getComponent, getConfigs } = this.props\n const Model = getComponent(\"Model\")\n\n let expanded\n if(this.props.layoutSelectors) {\n // If this is prop is present, we'll have deepLinking for it\n expanded = this.props.layoutSelectors.isShown(this.props.fullPath)\n }\n\n return
\n \n
\n }\n}\n","import React, { Component } from \"react\"\nimport Im, { Map } from \"immutable\"\nimport PropTypes from \"prop-types\"\n\nexport default class Models extends Component {\n static propTypes = {\n getComponent: PropTypes.func,\n specSelectors: PropTypes.object,\n specActions: PropTypes.object.isRequired,\n layoutSelectors: PropTypes.object,\n layoutActions: PropTypes.object,\n getConfigs: PropTypes.func.isRequired\n }\n\n getSchemaBasePath = () => {\n const isOAS3 = this.props.specSelectors.isOAS3()\n return isOAS3 ? [\"components\", \"schemas\"] : [\"definitions\"]\n }\n\n getCollapsedContent = () => {\n return \" \"\n }\n\n handleToggle = (name, isExpanded) => {\n const { layoutActions } = this.props\n layoutActions.show([...this.getSchemaBasePath(), name], isExpanded)\n if(isExpanded) {\n this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(), name])\n }\n }\n\n onLoadModels = (ref) => {\n if (ref) {\n this.props.layoutActions.readyToScroll(this.getSchemaBasePath(), ref)\n }\n }\n\n onLoadModel = (ref) => {\n if (ref) {\n const name = ref.getAttribute(\"data-name\")\n this.props.layoutActions.readyToScroll([...this.getSchemaBasePath(), name], ref)\n }\n }\n\n render(){\n let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props\n let definitions = specSelectors.definitions()\n let { docExpansion, defaultModelsExpandDepth } = getConfigs()\n if (!definitions.size || defaultModelsExpandDepth < 0) return null\n\n const specPathBase = this.getSchemaBasePath()\n let showModels = layoutSelectors.isShown(specPathBase, defaultModelsExpandDepth > 0 && docExpansion !== \"none\")\n const isOAS3 = specSelectors.isOAS3()\n\n const ModelWrapper = getComponent(\"ModelWrapper\")\n const Collapse = getComponent(\"Collapse\")\n const ModelCollapse = getComponent(\"ModelCollapse\")\n const JumpToPath = getComponent(\"JumpToPath\", true)\n\n return
\n

\n layoutActions.show(specPathBase, !showModels)}\n >\n {isOAS3 ? \"Schemas\" : \"Models\"}\n \n \n \n \n

\n \n {\n definitions.entrySeq().map(([name])=>{\n\n const fullPath = [...specPathBase, name]\n const specPath = Im.List(fullPath)\n\n const schemaValue = specSelectors.specResolvedSubtree(fullPath)\n const rawSchemaValue = specSelectors.specJson().getIn(fullPath)\n\n const schema = Map.isMap(schemaValue) ? schemaValue : Im.Map()\n const rawSchema = Map.isMap(rawSchemaValue) ? rawSchemaValue : Im.Map()\n\n const displayName = schema.get(\"title\") || rawSchema.get(\"title\") || name\n const isShown = layoutSelectors.isShown(fullPath, false)\n\n if( isShown && (schema.size === 0 && rawSchema.size > 0) ) {\n // Firing an action in a container render is not great,\n // but it works for now.\n this.props.specActions.requestResolvedSubtree(fullPath)\n }\n\n const content = \n\n const title = \n \n {displayName}\n \n \n\n return
\n \n 0 && isShown }\n >{content}\n
\n }).toArray()\n }\n
\n
\n }\n}\n","import React from \"react\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst EnumModel = ({ value, getComponent }) => {\n let ModelCollapse = getComponent(\"ModelCollapse\")\n let collapsedContent = Array [ { value.count() } ]\n return \n Enum:
\n \n [ { value.join(\", \") } ]\n \n
\n}\nEnumModel.propTypes = {\n value: ImPropTypes.iterable,\n getComponent: ImPropTypes.func\n}\n\nexport default EnumModel","import React, { Component, } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List } from \"immutable\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst braceOpen = \"{\"\nconst braceClose = \"}\"\nconst propClass = \"property\"\n\nexport default class ObjectModel extends Component {\n static propTypes = {\n schema: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n expanded: PropTypes.bool,\n onToggle: PropTypes.func,\n specSelectors: PropTypes.object.isRequired,\n name: PropTypes.string,\n displayName: PropTypes.string,\n isRef: PropTypes.bool,\n expandDepth: PropTypes.number,\n depth: PropTypes.number,\n specPath: ImPropTypes.list.isRequired,\n includeReadOnly: PropTypes.bool,\n includeWriteOnly: PropTypes.bool,\n }\n\n render(){\n let { schema, name, displayName, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props\n let { specSelectors,expandDepth, includeReadOnly, includeWriteOnly} = otherProps\n const { isOAS3 } = specSelectors\n\n if(!schema) {\n return null\n }\n\n const { showExtensions } = getConfigs()\n\n let description = schema.get(\"description\")\n let properties = schema.get(\"properties\")\n let additionalProperties = schema.get(\"additionalProperties\")\n let title = schema.get(\"title\") || displayName || name\n let requiredProperties = schema.get(\"required\")\n let infoProperties = schema\n .filter( ( v, key) => [\"maxProperties\", \"minProperties\", \"nullable\", \"example\"].indexOf(key) !== -1 )\n let deprecated = schema.get(\"deprecated\")\n\n const JumpToPath = getComponent(\"JumpToPath\", true)\n const Markdown = getComponent(\"Markdown\", true)\n const Model = getComponent(\"Model\")\n const ModelCollapse = getComponent(\"ModelCollapse\")\n const Property = getComponent(\"Property\")\n\n const JumpToPathSection = () => {\n return \n }\n const collapsedContent = (\n { braceOpen }...{ braceClose }\n {\n isRef ? : \"\"\n }\n )\n\n const anyOf = specSelectors.isOAS3() ? schema.get(\"anyOf\") : null\n const oneOf = specSelectors.isOAS3() ? schema.get(\"oneOf\") : null\n const not = specSelectors.isOAS3() ? schema.get(\"not\") : null\n\n const titleEl = title && \n { isRef && schema.get(\"$$ref\") && { schema.get(\"$$ref\") } }\n { title }\n \n\n return \n \n\n { braceOpen }\n {\n !isRef ? null : \n }\n \n {\n \n {\n !description ? null : \n \n \n \n }\n {\n !deprecated ? null :\n \n \n \n \n \n }\n {\n !(properties && properties.size) ? null : properties.entrySeq().filter(\n ([, value]) => {\n return (!value.get(\"readOnly\") || includeReadOnly) &&\n (!value.get(\"writeOnly\") || includeWriteOnly)\n }\n ).map(\n ([key, value]) => {\n let isDeprecated = isOAS3() && value.get(\"deprecated\")\n let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)\n\n let classNames = [\"property-row\"]\n\n if (isDeprecated) {\n classNames.push(\"deprecated\")\n }\n\n if (isRequired) {\n classNames.push(\"required\")\n }\n\n return (\n \n \n )\n }).toArray()\n }\n {\n // empty row befor extensions...\n !showExtensions ? null : \n }\n {\n !showExtensions ? null :\n schema.entrySeq().map(\n ([key, value]) => {\n if(key.slice(0,2) !== \"x-\") {\n return\n }\n\n const normalizedValue = !value ? null : value.toJS ? value.toJS() : value\n\n return (\n \n \n )\n }).toArray()\n }\n {\n !additionalProperties || !additionalProperties.size ? null\n : \n \n \n \n }\n {\n !anyOf ? null\n : \n \n \n \n }\n {\n !oneOf ? null\n : \n \n \n \n }\n {\n !not ? null\n : \n \n \n \n }\n
description:\n \n
\n deprecated:\n \n true\n
\n { key }{ isRequired && * }\n \n \n
 
\n { key }\n \n { JSON.stringify(normalizedValue) }\n
{ \"< * >:\" }\n \n
{ \"anyOf ->\" }\n {anyOf.map((schema, k) => {\n return
\n })}\n
{ \"oneOf ->\" }\n {oneOf.map((schema, k) => {\n return
\n })}\n
{ \"not ->\" }\n
\n \n
\n
\n }\n
\n { braceClose }\n \n {\n infoProperties.size ? infoProperties.entrySeq().map( ( [ key, v ] ) => ) : null\n }\n
\n }\n}\n","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport ImPropTypes from \"react-immutable-proptypes\"\n\nconst propClass = \"property\"\n\nexport default class ArrayModel extends Component {\n static propTypes = {\n schema: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n specSelectors: PropTypes.object.isRequired,\n name: PropTypes.string,\n displayName: PropTypes.string,\n required: PropTypes.bool,\n expandDepth: PropTypes.number,\n specPath: ImPropTypes.list.isRequired,\n depth: PropTypes.number,\n includeReadOnly: PropTypes.bool,\n includeWriteOnly: PropTypes.bool,\n }\n\n render(){\n let { getComponent, getConfigs, schema, depth, expandDepth, name, displayName, specPath } = this.props\n let description = schema.get(\"description\")\n let items = schema.get(\"items\")\n let title = schema.get(\"title\") || displayName || name\n let properties = schema.filter( ( v, key) => [\"type\", \"items\", \"description\", \"$$ref\"].indexOf(key) === -1 )\n\n const Markdown = getComponent(\"Markdown\", true)\n const ModelCollapse = getComponent(\"ModelCollapse\")\n const Model = getComponent(\"Model\")\n const Property = getComponent(\"Property\")\n\n const titleEl = title &&\n \n { title }\n \n\n /*\n Note: we set `name={null}` in below because we don't want\n the name of the current Model passed (and displayed) as the name of the array element Model\n */\n\n return \n \n [\n {\n properties.size ? properties.entrySeq().map( ( [ key, v ] ) => ) : null\n }\n {\n !description ? (properties.size ?
: null) :\n \n }\n \n \n \n ]\n
\n
\n }\n}\n","import React, { Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { getExtensions } from \"core/utils\"\n\nconst propClass = \"property primitive\"\n\nexport default class Primitive extends Component {\n static propTypes = {\n schema: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired,\n getConfigs: PropTypes.func.isRequired,\n name: PropTypes.string,\n displayName: PropTypes.string,\n depth: PropTypes.number,\n expandDepth: PropTypes.number\n }\n\n render() {\n let { schema, getComponent, getConfigs, name, displayName, depth, expandDepth } = this.props\n\n const { showExtensions } = getConfigs()\n\n if (!schema || !schema.get) {\n // don't render if schema isn't correctly formed\n return
\n }\n\n let type = schema.get(\"type\")\n let format = schema.get(\"format\")\n let xml = schema.get(\"xml\")\n let enumArray = schema.get(\"enum\")\n let title = schema.get(\"title\") || displayName || name\n let description = schema.get(\"description\")\n let extensions = getExtensions(schema)\n let properties = schema\n .filter((_, key) => [\"enum\", \"type\", \"format\", \"description\", \"$$ref\"].indexOf(key) === -1)\n .filterNot((_, key) => extensions.has(key))\n const Markdown = getComponent(\"Markdown\", true)\n const EnumModel = getComponent(\"EnumModel\")\n const Property = getComponent(\"Property\")\n const ModelCollapse = getComponent(\"ModelCollapse\")\n const titleEl = title &&\n \n {title}\n \n\n return \n = expandDepth} collapsedContent=\" \" hideSelfOnExpand={expandDepth !== depth}>\n \n {name && depth > 1 && {title}}\n {type}\n {format && (${format})}\n {\n properties.size ? properties.entrySeq().map(([key, v]) => ) : null\n }\n {\n showExtensions && extensions.size ? extensions.entrySeq().map(([key, v]) => ) : null\n }\n {\n !description ? null :\n \n }\n {\n xml && xml.size ? (
xml:\n {\n xml.entrySeq().map(([key, v]) =>
   {key}: {String(v)}
).toArray()\n }\n
) : null\n }\n {\n enumArray && \n }\n
\n
\n
\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const Property = ({ propKey, propVal, propClass }) => {\n return (\n \n
{ propKey }: { String(propVal) }
\n )\n}\nProperty.propTypes = {\n propKey: PropTypes.string,\n propVal: PropTypes.any,\n propClass: PropTypes.string\n}\n\nexport default Property\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class TryItOutButton extends React.Component {\n\n static propTypes = {\n onTryoutClick: PropTypes.func,\n onResetClick: PropTypes.func,\n onCancelClick: PropTypes.func,\n enabled: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form\n hasUserEditedBody: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form\n isOAS3: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form\n };\n\n static defaultProps = {\n onTryoutClick: Function.prototype,\n onCancelClick: Function.prototype,\n onResetClick: Function.prototype,\n enabled: false,\n hasUserEditedBody: false,\n isOAS3: false,\n };\n\n render() {\n const { onTryoutClick, onCancelClick, onResetClick, enabled, hasUserEditedBody, isOAS3 } = this.props\n\n const showReset = isOAS3 && hasUserEditedBody\n return (\n
\n {\n enabled ? \n : \n\n }\n {\n showReset && \n }\n
\n )\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class VersionPragmaFilter extends React.PureComponent {\n static propTypes = {\n isSwagger2: PropTypes.bool.isRequired,\n isOAS3: PropTypes.bool.isRequired,\n bypass: PropTypes.bool,\n alsoShow: PropTypes.element,\n children: PropTypes.any,\n }\n\n static defaultProps = {\n alsoShow: null,\n children: null,\n bypass: false,\n }\n\n render() {\n const { bypass, isSwagger2, isOAS3, alsoShow } = this.props\n\n if(bypass) {\n return
{ this.props.children }
\n }\n\n if(isSwagger2 && isOAS3) {\n return
\n {alsoShow}\n
\n
\n

Unable to render this definition

\n

swagger and openapi fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields.

\n

Supported version fields are swagger: {\"\\\"2.0\\\"\"} and those that match openapi: 3.0.n (for example, openapi: 3.0.0).

\n
\n
\n
\n }\n\n if(!isSwagger2 && !isOAS3) {\n return
\n {alsoShow}\n
\n
\n

Unable to render this definition

\n

The provided definition does not specify a valid version field.

\n

Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: {\"\\\"2.0\\\"\"} and those that match openapi: 3.0.n (for example, openapi: 3.0.0).

\n
\n
\n
\n }\n\n return
{ this.props.children }
\n }\n}\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nconst VersionStamp = ({ version }) => {\n return
 { version } 
\n}\n\nVersionStamp.propTypes = {\n version: PropTypes.string.isRequired\n}\n\nexport default VersionStamp\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport const DeepLink = ({ enabled, path, text }) => {\n return (\n e.preventDefault() : null}\n href={enabled ? `#/${path}` : null}>\n {text}\n \n )\n}\nDeepLink.propTypes = {\n enabled: PropTypes.bool,\n isShown: PropTypes.bool,\n path: PropTypes.string,\n text: PropTypes.node\n}\n\nexport default DeepLink\n","import React from \"react\"\nconst SvgAssets = () =>\n
\n \n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n
\n\nexport default SvgAssets\n","import React from \"react\"\nimport PropTypes from \"prop-types\"\n\nexport default class BaseLayout extends React.Component {\n\n static propTypes = {\n errSelectors: PropTypes.object.isRequired,\n errActions: PropTypes.object.isRequired,\n specSelectors: PropTypes.object.isRequired,\n oas3Selectors: PropTypes.object.isRequired,\n oas3Actions: PropTypes.object.isRequired,\n getComponent: PropTypes.func.isRequired\n }\n\n render() {\n let {errSelectors, specSelectors, getComponent} = this.props\n\n let SvgAssets = getComponent(\"SvgAssets\")\n let InfoContainer = getComponent(\"InfoContainer\", true)\n let VersionPragmaFilter = getComponent(\"VersionPragmaFilter\")\n let Operations = getComponent(\"operations\", true)\n let Models = getComponent(\"Models\", true)\n let Row = getComponent(\"Row\")\n let Col = getComponent(\"Col\")\n let Errors = getComponent(\"errors\", true)\n\n const ServersContainer = getComponent(\"ServersContainer\", true)\n const SchemesContainer = getComponent(\"SchemesContainer\", true)\n const AuthorizeBtnContainer = getComponent(\"AuthorizeBtnContainer\", true)\n const FilterContainer = getComponent(\"FilterContainer\", true)\n let isSwagger2 = specSelectors.isSwagger2()\n let isOAS3 = specSelectors.isOAS3()\n\n const isSpecEmpty = !specSelectors.specStr()\n\n const loadingStatus = specSelectors.loadingStatus()\n\n let loadingMessage = null\n\n if(loadingStatus === \"loading\") {\n loadingMessage =
\n
\n
\n
\n
\n }\n\n if(loadingStatus === \"failed\") {\n loadingMessage =
\n
\n

Failed to load API definition.

\n \n
\n
\n }\n\n if (loadingStatus === \"failedConfig\") {\n const lastErr = errSelectors.lastError()\n const lastErrMsg = lastErr ? lastErr.get(\"message\") : \"\"\n loadingMessage =
\n
\n

Failed to load remote configuration.

\n

{lastErrMsg}

\n
\n
\n }\n\n if(!loadingMessage && isSpecEmpty) {\n loadingMessage =

No API definition provided.

\n }\n\n if(loadingMessage) {\n return
\n
\n {loadingMessage}\n
\n
\n }\n\n const servers = specSelectors.servers()\n const schemes = specSelectors.schemes()\n\n const hasServers = servers && servers.size\n const hasSchemes = schemes && schemes.size\n const hasSecurityDefinitions = !!specSelectors.securityDefinitions()\n\n return (\n
\n \n }>\n \n \n \n \n \n \n\n {hasServers || hasSchemes || hasSecurityDefinitions ? (\n
\n \n {hasServers ? () : null}\n {hasSchemes ? () : null}\n {hasSecurityDefinitions ? () : null}\n \n
\n ) : null}\n\n \n\n \n \n \n \n \n \n \n \n \n \n
\n
\n )\n }\n}\n","var x = y => { var x = {}; __webpack_require__.d(x, y); return x; }\nvar y = x => () => x\nconst __WEBPACK_NAMESPACE_OBJECT__ = x({ [\"default\"]: () => __WEBPACK_EXTERNAL_MODULE_react_debounce_input_7ed3e068__[\"default\"] });","import React, { PureComponent, Component } from \"react\"\nimport PropTypes from \"prop-types\"\nimport { List, fromJS } from \"immutable\"\nimport cx from \"classnames\"\nimport ImPropTypes from \"react-immutable-proptypes\"\nimport DebounceInput from \"react-debounce-input\"\nimport { stringify, getSampleSchema } from \"core/utils\"\n//import \"less/json-schema-form\"\n\nconst noop = ()=> {}\nconst JsonSchemaPropShape = {\n getComponent: PropTypes.func.isRequired,\n value: PropTypes.any,\n onChange: PropTypes.func,\n keyName: PropTypes.any,\n fn: PropTypes.object.isRequired,\n schema: PropTypes.object,\n errors: ImPropTypes.list,\n required: PropTypes.bool,\n dispatchInitialValue: PropTypes.bool,\n description: PropTypes.any,\n disabled: PropTypes.bool,\n}\n\nconst JsonSchemaDefaultProps = {\n value: \"\",\n onChange: noop,\n schema: {},\n keyName: \"\",\n required: false,\n errors: List()\n}\n\nexport class JsonSchemaForm extends Component {\n\n static propTypes = JsonSchemaPropShape\n static defaultProps = JsonSchemaDefaultProps\n\n componentDidMount() {\n const { dispatchInitialValue, value, onChange } = this.props\n if(dispatchInitialValue) {\n onChange(value)\n } else if(dispatchInitialValue === false) {\n onChange(\"\")\n }\n }\n\n render() {\n let { schema, errors, value, onChange, getComponent, fn, disabled } = this.props\n const format = schema && schema.get ? schema.get(\"format\") : null\n const type = schema && schema.get ? schema.get(\"type\") : null\n\n let getComponentSilently = (name) => getComponent(name, false, { failSilently: true })\n let Comp = type ? format ?\n getComponentSilently(`JsonSchema_${type}_${format}`) :\n getComponentSilently(`JsonSchema_${type}`) :\n getComponent(\"JsonSchema_string\")\n if (!Comp) {\n Comp = getComponent(\"JsonSchema_string\")\n }\n return \n }\n}\n\nexport class JsonSchema_string extends Component {\n static propTypes = JsonSchemaPropShape\n static defaultProps = JsonSchemaDefaultProps\n onChange = (e) => {\n const value = this.props.schema && this.props.schema.get(\"type\") === \"file\" ? e.target.files[0] : e.target.value\n this.props.onChange(value, this.props.keyName)\n }\n onEnumChange = (val) => this.props.onChange(val)\n render() {\n let { getComponent, value, schema, errors, required, description, disabled } = this.props\n const enumValue = schema && schema.get ? schema.get(\"enum\") : null\n const format = schema && schema.get ? schema.get(\"format\") : null\n const type = schema && schema.get ? schema.get(\"type\") : null\n const schemaIn = schema && schema.get ? schema.get(\"in\") : null\n if (!value) {\n value = \"\" // value should not be null; this fixes a Debounce error\n }\n errors = errors.toJS ? errors.toJS() : []\n\n if ( enumValue ) {\n const Select = getComponent(\"Select\")\n return (\n )\n }\n else {\n return (\n \n )\n }\n }\n}\n\nexport class JsonSchema_array extends PureComponent {\n\n static propTypes = JsonSchemaPropShape\n static defaultProps = JsonSchemaDefaultProps\n\n constructor(props, context) {\n super(props, context)\n this.state = { value: valueOrEmptyList(props.value), schema: props.schema}\n }\n\n UNSAFE_componentWillReceiveProps(props) {\n const value = valueOrEmptyList(props.value)\n if(value !== this.state.value)\n this.setState({ value })\n\n if(props.schema !== this.state.schema)\n this.setState({ schema: props.schema })\n }\n\n onChange = () => {\n this.props.onChange(this.state.value)\n }\n\n onItemChange = (itemVal, i) => {\n this.setState(({ value }) => ({\n value: value.set(i, itemVal)\n }), this.onChange)\n }\n\n removeItem = (i) => {\n this.setState(({ value }) => ({\n value: value.delete(i)\n }), this.onChange)\n }\n\n addItem = () => {\n let newValue = valueOrEmptyList(this.state.value)\n this.setState(() => ({\n value: newValue.push(getSampleSchema(this.state.schema.get(\"items\"), false, {\n includeWriteOnly: true\n }))\n }), this.onChange)\n }\n\n onEnumChange = (value) => {\n this.setState(() => ({\n value: value\n }), this.onChange)\n }\n\n render() {\n let { getComponent, required, schema, errors, fn, disabled } = this.props\n\n errors = errors.toJS ? errors.toJS() : Array.isArray(errors) ? errors : []\n const arrayErrors = errors.filter(e => typeof e === \"string\")\n const needsRemoveError = errors.filter(e => e.needRemove !== undefined)\n .map(e => e.error)\n const value = this.state.value // expect Im List\n const shouldRenderValue =\n value && value.count && value.count() > 0 ? true : false\n const schemaItemsEnum = schema.getIn([\"items\", \"enum\"])\n const schemaItemsType = schema.getIn([\"items\", \"type\"])\n const schemaItemsFormat = schema.getIn([\"items\", \"format\"])\n const schemaItemsSchema = schema.get(\"items\")\n let ArrayItemsComponent\n let isArrayItemText = false\n let isArrayItemFile = (schemaItemsType === \"file\" || (schemaItemsType === \"string\" && schemaItemsFormat === \"binary\")) ? true : false\n if (schemaItemsType && schemaItemsFormat) {\n ArrayItemsComponent = getComponent(`JsonSchema_${schemaItemsType}_${schemaItemsFormat}`)\n } else if (schemaItemsType === \"boolean\" || schemaItemsType === \"array\" || schemaItemsType === \"object\") {\n ArrayItemsComponent = getComponent(`JsonSchema_${schemaItemsType}`)\n }\n // if ArrayItemsComponent not assigned or does not exist,\n // use default schemaItemsType === \"string\" & JsonSchemaArrayItemText component\n if (!ArrayItemsComponent && !isArrayItemFile) {\n isArrayItemText = true\n }\n\n if ( schemaItemsEnum ) {\n const Select = getComponent(\"Select\")\n return ()\n }\n}\n\nexport class JsonSchema_boolean extends Component {\n static propTypes = JsonSchemaPropShape\n static defaultProps = JsonSchemaDefaultProps\n\n onEnumChange = (val) => this.props.onChange(val)\n render() {\n let { getComponent, value, errors, schema, required, disabled } = this.props\n errors = errors.toJS ? errors.toJS() : []\n let enumValue = schema && schema.get ? schema.get(\"enum\") : null\n let allowEmptyValue = !enumValue || !required\n let booleanValue = !enumValue && fromJS([\"true\", \"false\"])\n const Select = getComponent(\"Select\")\n\n return ( + + + +
+
+ +
+ +
+
+ +
+
+ + +
+
+ + + + + + +

IntelOwl Centralised Documentation

+

Welcome to the IntelOwl Centralised Documentation. Here you will be able to find all documentation for all projects under IntelOwl.

+

Introduction

+

Intel Owl is an Open Source Intelligence, or OSINT solution, to get Threat Intelligence data about a specific digital artifact from a single API at scale. It integrates a high number of services available online and a lot of cutting-edge malware analysis tools. It is for everyone who needs a single point to query for info about a specific file or observable. If you are a Security Analyst, do not waste any more time in performing enrichment tasks! IntelOwl saves your time and allows you to concentrate on more serious tasks.

+

Getting started

+
+
    +
  • +

    Introduction + More

    +
  • +
  • +

    Installation + More

    +
  • +
  • +

    Contribute + More

    +
  • +
  • +

    Usage + More

    +
  • +
  • +

    Advanced Usage + More

    +
  • +
  • +

    Advanced Configuration + More

    +
  • +
+
+
+

Need more help?

+

We are doing our best to keep this documentation complete, accurate and up to date.

+

If you still have questions or you find something which is not sufficiently explained, join the IntelOwl channel under HoneyNet Community on Slack.

+
+

+


+ +
+ +
+ + + +
+
+
+ + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000..901a2cb Binary files /dev/null and b/objects.inv differ diff --git a/pyintelowl/IntelOwlClass/index.html b/pyintelowl/IntelOwlClass/index.html new file mode 100644 index 0000000..c893ec7 --- /dev/null +++ b/pyintelowl/IntelOwlClass/index.html @@ -0,0 +1,6851 @@ + + + + + + + + + + + + + +IntelOwlClass - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

IntelOwlClass

+

IntelOwl Class

+
+ +
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
  19
+  20
+  21
+  22
+  23
+  24
+  25
+  26
+  27
+  28
+  29
+  30
+  31
+  32
+  33
+  34
+  35
+  36
+  37
+  38
+  39
+  40
+  41
+  42
+  43
+  44
+  45
+  46
+  47
+  48
+  49
+  50
+  51
+  52
+  53
+  54
+  55
+  56
+  57
+  58
+  59
+  60
+  61
+  62
+  63
+  64
+  65
+  66
+  67
+  68
+  69
+  70
+  71
+  72
+  73
+  74
+  75
+  76
+  77
+  78
+  79
+  80
+  81
+  82
+  83
+  84
+  85
+  86
+  87
+  88
+  89
+  90
+  91
+  92
+  93
+  94
+  95
+  96
+  97
+  98
+  99
+ 100
+ 101
+ 102
+ 103
+ 104
+ 105
+ 106
+ 107
+ 108
+ 109
+ 110
+ 111
+ 112
+ 113
+ 114
+ 115
+ 116
+ 117
+ 118
+ 119
+ 120
+ 121
+ 122
+ 123
+ 124
+ 125
+ 126
+ 127
+ 128
+ 129
+ 130
+ 131
+ 132
+ 133
+ 134
+ 135
+ 136
+ 137
+ 138
+ 139
+ 140
+ 141
+ 142
+ 143
+ 144
+ 145
+ 146
+ 147
+ 148
+ 149
+ 150
+ 151
+ 152
+ 153
+ 154
+ 155
+ 156
+ 157
+ 158
+ 159
+ 160
+ 161
+ 162
+ 163
+ 164
+ 165
+ 166
+ 167
+ 168
+ 169
+ 170
+ 171
+ 172
+ 173
+ 174
+ 175
+ 176
+ 177
+ 178
+ 179
+ 180
+ 181
+ 182
+ 183
+ 184
+ 185
+ 186
+ 187
+ 188
+ 189
+ 190
+ 191
+ 192
+ 193
+ 194
+ 195
+ 196
+ 197
+ 198
+ 199
+ 200
+ 201
+ 202
+ 203
+ 204
+ 205
+ 206
+ 207
+ 208
+ 209
+ 210
+ 211
+ 212
+ 213
+ 214
+ 215
+ 216
+ 217
+ 218
+ 219
+ 220
+ 221
+ 222
+ 223
+ 224
+ 225
+ 226
+ 227
+ 228
+ 229
+ 230
+ 231
+ 232
+ 233
+ 234
+ 235
+ 236
+ 237
+ 238
+ 239
+ 240
+ 241
+ 242
+ 243
+ 244
+ 245
+ 246
+ 247
+ 248
+ 249
+ 250
+ 251
+ 252
+ 253
+ 254
+ 255
+ 256
+ 257
+ 258
+ 259
+ 260
+ 261
+ 262
+ 263
+ 264
+ 265
+ 266
+ 267
+ 268
+ 269
+ 270
+ 271
+ 272
+ 273
+ 274
+ 275
+ 276
+ 277
+ 278
+ 279
+ 280
+ 281
+ 282
+ 283
+ 284
+ 285
+ 286
+ 287
+ 288
+ 289
+ 290
+ 291
+ 292
+ 293
+ 294
+ 295
+ 296
+ 297
+ 298
+ 299
+ 300
+ 301
+ 302
+ 303
+ 304
+ 305
+ 306
+ 307
+ 308
+ 309
+ 310
+ 311
+ 312
+ 313
+ 314
+ 315
+ 316
+ 317
+ 318
+ 319
+ 320
+ 321
+ 322
+ 323
+ 324
+ 325
+ 326
+ 327
+ 328
+ 329
+ 330
+ 331
+ 332
+ 333
+ 334
+ 335
+ 336
+ 337
+ 338
+ 339
+ 340
+ 341
+ 342
+ 343
+ 344
+ 345
+ 346
+ 347
+ 348
+ 349
+ 350
+ 351
+ 352
+ 353
+ 354
+ 355
+ 356
+ 357
+ 358
+ 359
+ 360
+ 361
+ 362
+ 363
+ 364
+ 365
+ 366
+ 367
+ 368
+ 369
+ 370
+ 371
+ 372
+ 373
+ 374
+ 375
+ 376
+ 377
+ 378
+ 379
+ 380
+ 381
+ 382
+ 383
+ 384
+ 385
+ 386
+ 387
+ 388
+ 389
+ 390
+ 391
+ 392
+ 393
+ 394
+ 395
+ 396
+ 397
+ 398
+ 399
+ 400
+ 401
+ 402
+ 403
+ 404
+ 405
+ 406
+ 407
+ 408
+ 409
+ 410
+ 411
+ 412
+ 413
+ 414
+ 415
+ 416
+ 417
+ 418
+ 419
+ 420
+ 421
+ 422
+ 423
+ 424
+ 425
+ 426
+ 427
+ 428
+ 429
+ 430
+ 431
+ 432
+ 433
+ 434
+ 435
+ 436
+ 437
+ 438
+ 439
+ 440
+ 441
+ 442
+ 443
+ 444
+ 445
+ 446
+ 447
+ 448
+ 449
+ 450
+ 451
+ 452
+ 453
+ 454
+ 455
+ 456
+ 457
+ 458
+ 459
+ 460
+ 461
+ 462
+ 463
+ 464
+ 465
+ 466
+ 467
+ 468
+ 469
+ 470
+ 471
+ 472
+ 473
+ 474
+ 475
+ 476
+ 477
+ 478
+ 479
+ 480
+ 481
+ 482
+ 483
+ 484
+ 485
+ 486
+ 487
+ 488
+ 489
+ 490
+ 491
+ 492
+ 493
+ 494
+ 495
+ 496
+ 497
+ 498
+ 499
+ 500
+ 501
+ 502
+ 503
+ 504
+ 505
+ 506
+ 507
+ 508
+ 509
+ 510
+ 511
+ 512
+ 513
+ 514
+ 515
+ 516
+ 517
+ 518
+ 519
+ 520
+ 521
+ 522
+ 523
+ 524
+ 525
+ 526
+ 527
+ 528
+ 529
+ 530
+ 531
+ 532
+ 533
+ 534
+ 535
+ 536
+ 537
+ 538
+ 539
+ 540
+ 541
+ 542
+ 543
+ 544
+ 545
+ 546
+ 547
+ 548
+ 549
+ 550
+ 551
+ 552
+ 553
+ 554
+ 555
+ 556
+ 557
+ 558
+ 559
+ 560
+ 561
+ 562
+ 563
+ 564
+ 565
+ 566
+ 567
+ 568
+ 569
+ 570
+ 571
+ 572
+ 573
+ 574
+ 575
+ 576
+ 577
+ 578
+ 579
+ 580
+ 581
+ 582
+ 583
+ 584
+ 585
+ 586
+ 587
+ 588
+ 589
+ 590
+ 591
+ 592
+ 593
+ 594
+ 595
+ 596
+ 597
+ 598
+ 599
+ 600
+ 601
+ 602
+ 603
+ 604
+ 605
+ 606
+ 607
+ 608
+ 609
+ 610
+ 611
+ 612
+ 613
+ 614
+ 615
+ 616
+ 617
+ 618
+ 619
+ 620
+ 621
+ 622
+ 623
+ 624
+ 625
+ 626
+ 627
+ 628
+ 629
+ 630
+ 631
+ 632
+ 633
+ 634
+ 635
+ 636
+ 637
+ 638
+ 639
+ 640
+ 641
+ 642
+ 643
+ 644
+ 645
+ 646
+ 647
+ 648
+ 649
+ 650
+ 651
+ 652
+ 653
+ 654
+ 655
+ 656
+ 657
+ 658
+ 659
+ 660
+ 661
+ 662
+ 663
+ 664
+ 665
+ 666
+ 667
+ 668
+ 669
+ 670
+ 671
+ 672
+ 673
+ 674
+ 675
+ 676
+ 677
+ 678
+ 679
+ 680
+ 681
+ 682
+ 683
+ 684
+ 685
+ 686
+ 687
+ 688
+ 689
+ 690
+ 691
+ 692
+ 693
+ 694
+ 695
+ 696
+ 697
+ 698
+ 699
+ 700
+ 701
+ 702
+ 703
+ 704
+ 705
+ 706
+ 707
+ 708
+ 709
+ 710
+ 711
+ 712
+ 713
+ 714
+ 715
+ 716
+ 717
+ 718
+ 719
+ 720
+ 721
+ 722
+ 723
+ 724
+ 725
+ 726
+ 727
+ 728
+ 729
+ 730
+ 731
+ 732
+ 733
+ 734
+ 735
+ 736
+ 737
+ 738
+ 739
+ 740
+ 741
+ 742
+ 743
+ 744
+ 745
+ 746
+ 747
+ 748
+ 749
+ 750
+ 751
+ 752
+ 753
+ 754
+ 755
+ 756
+ 757
+ 758
+ 759
+ 760
+ 761
+ 762
+ 763
+ 764
+ 765
+ 766
+ 767
+ 768
+ 769
+ 770
+ 771
+ 772
+ 773
+ 774
+ 775
+ 776
+ 777
+ 778
+ 779
+ 780
+ 781
+ 782
+ 783
+ 784
+ 785
+ 786
+ 787
+ 788
+ 789
+ 790
+ 791
+ 792
+ 793
+ 794
+ 795
+ 796
+ 797
+ 798
+ 799
+ 800
+ 801
+ 802
+ 803
+ 804
+ 805
+ 806
+ 807
+ 808
+ 809
+ 810
+ 811
+ 812
+ 813
+ 814
+ 815
+ 816
+ 817
+ 818
+ 819
+ 820
+ 821
+ 822
+ 823
+ 824
+ 825
+ 826
+ 827
+ 828
+ 829
+ 830
+ 831
+ 832
+ 833
+ 834
+ 835
+ 836
+ 837
+ 838
+ 839
+ 840
+ 841
+ 842
+ 843
+ 844
+ 845
+ 846
+ 847
+ 848
+ 849
+ 850
+ 851
+ 852
+ 853
+ 854
+ 855
+ 856
+ 857
+ 858
+ 859
+ 860
+ 861
+ 862
+ 863
+ 864
+ 865
+ 866
+ 867
+ 868
+ 869
+ 870
+ 871
+ 872
+ 873
+ 874
+ 875
+ 876
+ 877
+ 878
+ 879
+ 880
+ 881
+ 882
+ 883
+ 884
+ 885
+ 886
+ 887
+ 888
+ 889
+ 890
+ 891
+ 892
+ 893
+ 894
+ 895
+ 896
+ 897
+ 898
+ 899
+ 900
+ 901
+ 902
+ 903
+ 904
+ 905
+ 906
+ 907
+ 908
+ 909
+ 910
+ 911
+ 912
+ 913
+ 914
+ 915
+ 916
+ 917
+ 918
+ 919
+ 920
+ 921
+ 922
+ 923
+ 924
+ 925
+ 926
+ 927
+ 928
+ 929
+ 930
+ 931
+ 932
+ 933
+ 934
+ 935
+ 936
+ 937
+ 938
+ 939
+ 940
+ 941
+ 942
+ 943
+ 944
+ 945
+ 946
+ 947
+ 948
+ 949
+ 950
+ 951
+ 952
+ 953
+ 954
+ 955
+ 956
+ 957
+ 958
+ 959
+ 960
+ 961
+ 962
+ 963
+ 964
+ 965
+ 966
+ 967
+ 968
+ 969
+ 970
+ 971
+ 972
+ 973
+ 974
+ 975
+ 976
+ 977
+ 978
+ 979
+ 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
class IntelOwl:
+    logger: logging.Logger
+
+    def __init__(
+        self,
+        token: str,
+        instance_url: str,
+        certificate: str = None,
+        proxies: dict = None,
+        logger: logging.Logger = None,
+        cli: bool = False,
+    ):
+        self.token = token
+        self.instance = instance_url
+        self.certificate = certificate
+        if logger:
+            self.logger = logger
+        else:
+            self.logger = logging.getLogger(__name__)
+        if proxies and not isinstance(proxies, dict):
+            raise TypeError("proxies param must be a dictionary")
+        self.proxies = proxies
+        self.cli = cli
+
+    @property
+    def session(self) -> requests.Session:
+        """
+        Internal use only.
+        """
+        if not hasattr(self, "_session"):
+            session = requests.Session()
+            if self.certificate is not True:
+                session.verify = self.certificate
+            if self.proxies:
+                session.proxies = self.proxies
+            session.headers.update(
+                {
+                    "Authorization": f"Token {self.token}",
+                    "User-Agent": f"PyIntelOwl/{__version__}",
+                }
+            )
+            self._session = session
+
+        return self._session
+
+    def __make_request(
+        self,
+        method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] = "GET",
+        *args,
+        **kwargs,
+    ) -> requests.Response:
+        """
+        For internal use only.
+        """
+        response: requests.Response = None
+        requests_function_map: Dict[str, Callable] = {
+            "GET": self.session.get,
+            "POST": self.session.post,
+            "PUT": self.session.put,
+            "PATCH": self.session.patch,
+            "DELETE": self.session.delete,
+        }
+        func = requests_function_map.get(method, None)
+        if not func:
+            raise RuntimeError(f"Unsupported method name: {method}")
+
+        try:
+            response = func(*args, **kwargs)
+            self.logger.debug(
+                msg=(response.url, response.status_code, response.content)
+            )
+            response.raise_for_status()
+        except Exception as e:
+            raise IntelOwlClientException(e, response=response)
+
+        return response
+
+    def ask_analysis_availability(
+        self,
+        md5: str,
+        analyzers: List[str] = None,
+        check_reported_analysis_too: bool = False,
+        minutes_ago: int = None,
+    ) -> Dict:
+        """Search for already available analysis.\n
+        Endpoint: ``/api/ask_analysis_availability``
+
+        Args:
+            md5 (str): md5sum of the observable or file
+            analyzers (List[str], optional):
+            list of analyzers to trigger.
+            Defaults to `None` meaning automatically select all configured analyzers.
+            check_reported_analysis_too (bool, optional):
+            Check against all existing jobs. Defaults to ``False``.
+            minutes_ago (int, optional):
+            number of minutes to check back for analysis.
+            Default is None so the check does not have any time limits.
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Dict: JSON body
+        """
+        if not analyzers:
+            analyzers = []
+        data = {"md5": md5, "analyzers": analyzers}
+        if not check_reported_analysis_too:
+            data["running_only"] = True
+        if minutes_ago:
+            data["minutes_ago"] = int(minutes_ago)
+        url = self.instance + "/api/ask_analysis_availability"
+        response = self.__make_request("POST", url=url, data=data)
+        answer = response.json()
+        status, job_id = answer.get("status", None), answer.get("job_id", None)
+        # check sanity cases
+        if not status:
+            raise IntelOwlClientException(
+                "API ask_analysis_availability gave result without status ?"
+                f" Response: {answer}"
+            )
+        if status != "not_available" and not job_id:
+            raise IntelOwlClientException(
+                "API ask_analysis_availability gave result without job_id ?"
+                f" Response: {answer}"
+            )
+        return answer
+
+    def send_file_analysis_request(
+        self,
+        filename: str,
+        binary: bytes,
+        tlp: TLPType = "CLEAR",
+        analyzers_requested: List[str] = None,
+        connectors_requested: List[str] = None,
+        runtime_configuration: Dict = None,
+        tags_labels: List[str] = None,
+    ) -> Dict:
+        """Send analysis request for a file.\n
+        Endpoint: ``/api/analyze_file``
+
+        Args:
+
+            filename (str):
+                Filename
+            binary (bytes):
+                File contents as bytes
+            analyzers_requested (List[str], optional):
+                List of analyzers to invoke
+                Defaults to ``[]`` i.e. all analyzers.
+            connectors_requested (List[str], optional):
+                List of specific connectors to invoke.
+                Defaults to ``[]`` i.e. all connectors.
+            tlp (str, optional):
+                TLP for the analysis.
+                (options: ``CLEAR, GREEN, AMBER, RED``).
+            runtime_configuration (Dict, optional):
+                Overwrite configuration for analyzers. Defaults to ``{}``.
+            tags_labels (List[str], optional):
+                List of tag labels to assign (creates non-existing tags)
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Dict: JSON body
+        """
+        try:
+            if not tlp:
+                tlp = "CLEAR"
+            if not analyzers_requested:
+                analyzers_requested = []
+            if not connectors_requested:
+                connectors_requested = []
+            if not tags_labels:
+                tags_labels = []
+            if not runtime_configuration:
+                runtime_configuration = {}
+            data = {
+                "file_name": filename,
+                "analyzers_requested": analyzers_requested,
+                "connectors_requested": connectors_requested,
+                "tlp": tlp,
+                "tags_labels": tags_labels,
+            }
+            if runtime_configuration:
+                data["runtime_configuration"] = json.dumps(runtime_configuration)
+            files = {"file": (filename, binary)}
+            answer = self.__send_analysis_request(data=data, files=files)
+        except Exception as e:
+            raise IntelOwlClientException(e)
+        return answer
+
+    def send_file_analysis_playbook_request(
+        self,
+        filename: str,
+        binary: bytes,
+        playbook_requested: str,
+        tlp: TLPType = "CLEAR",
+        runtime_configuration: Dict = None,
+        tags_labels: List[str] = None,
+    ) -> Dict:
+        """Send playbook analysis request for a file.\n
+        Endpoint: ``/api/playbook/analyze_multiple_files``
+
+        Args:
+
+            filename (str):
+                Filename
+            binary (bytes):
+                File contents as bytes
+            playbook_requested (str, optional):
+            tlp (str, optional):
+                TLP for the analysis.
+                (options: ``WHITE, GREEN, AMBER, RED``).
+            runtime_configuration (Dict, optional):
+                Overwrite configuration for analyzers. Defaults to ``{}``.
+            tags_labels (List[str], optional):
+                List of tag labels to assign (creates non-existing tags)
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Dict: JSON body
+        """
+        try:
+            if not tags_labels:
+                tags_labels = []
+            if not runtime_configuration:
+                runtime_configuration = {}
+            data = {
+                "playbook_requested": playbook_requested,
+                "tags_labels": tags_labels,
+            }
+            # send this value only if populated,
+            # otherwise the backend would give you 400
+            if tlp:
+                data["tlp"] = tlp
+
+            if runtime_configuration:
+                data["runtime_configuration"] = json.dumps(runtime_configuration)
+            # `files` is wanted to be different from the other
+            # /api/analyze_file endpoint
+            # because the server is using different serializers
+            files = {"files": (filename, binary)}
+            answer = self.__send_analysis_request(
+                data=data, files=files, playbook_mode=True
+            )
+        except Exception as e:
+            raise IntelOwlClientException(e)
+        return answer
+
+    def send_observable_analysis_request(
+        self,
+        observable_name: str,
+        tlp: TLPType = "CLEAR",
+        analyzers_requested: List[str] = None,
+        connectors_requested: List[str] = None,
+        runtime_configuration: Dict = None,
+        tags_labels: List[str] = None,
+        observable_classification: str = None,
+    ) -> Dict:
+        """Send analysis request for an observable.\n
+        Endpoint: ``/api/analyze_observable``
+
+        Args:
+            observable_name (str):
+                Observable value
+            analyzers_requested (List[str], optional):
+                List of analyzers to invoke
+                Defaults to ``[]`` i.e. all analyzers.
+            connectors_requested (List[str], optional):
+                List of specific connectors to invoke.
+                Defaults to ``[]`` i.e. all connectors.
+            tlp (str, optional):
+                TLP for the analysis.
+                (options: ``CLEAR, GREEN, AMBER, RED``).
+            runtime_configuration (Dict, optional):
+                Overwrite configuration for analyzers. Defaults to ``{}``.
+            tags_labels (List[str], optional):
+                List of tag labels to assign (creates non-existing tags)
+            observable_classification (str):
+                Observable classification, Default to None.
+                By default launch analysis with an automatic classification.
+                (options: ``url, domain, hash, ip, generic``)
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+            IntelOwlClientException: on wrong observable_classification
+
+        Returns:
+            Dict: JSON body
+        """
+        try:
+            if not tlp:
+                tlp = "CLEAR"
+            if not analyzers_requested:
+                analyzers_requested = []
+            if not connectors_requested:
+                connectors_requested = []
+            if not tags_labels:
+                tags_labels = []
+            if not runtime_configuration:
+                runtime_configuration = {}
+            if not observable_classification:
+                observable_classification = self._get_observable_classification(
+                    observable_name
+                )
+            elif observable_classification not in [
+                "generic",
+                "hash",
+                "ip",
+                "domain",
+                "url",
+            ]:
+                raise IntelOwlClientException(
+                    "Observable classification only handle"
+                    " 'generic', 'hash', 'ip', 'domain' and 'url' "
+                )
+            data = {
+                "observable_name": observable_name,
+                "observable_classification": observable_classification,
+                "analyzers_requested": analyzers_requested,
+                "connectors_requested": connectors_requested,
+                "tlp": tlp,
+                "tags_labels": tags_labels,
+                "runtime_configuration": runtime_configuration,
+            }
+            answer = self.__send_analysis_request(data=data, files=None)
+        except Exception as e:
+            raise IntelOwlClientException(e)
+        return answer
+
+    def send_observable_analysis_playbook_request(
+        self,
+        observable_name: str,
+        playbook_requested: str,
+        tlp: TLPType = "CLEAR",
+        runtime_configuration: Dict = None,
+        tags_labels: List[str] = None,
+        observable_classification: str = None,
+    ) -> Dict:
+        """Send playbook analysis request for an observable.\n
+        Endpoint: ``/api/playbook/analyze_multiple_observables``
+
+        Args:
+            observable_name (str):
+                Observable value
+            playbook_requested str:
+            tlp (str, optional):
+                TLP for the analysis.
+                (options: ``WHITE, GREEN, AMBER, RED``).
+            runtime_configuration (Dict, optional):
+                Overwrite configuration for analyzers. Defaults to ``{}``.
+            tags_labels (List[str], optional):
+                List of tag labels to assign (creates non-existing tags)
+            observable_classification (str):
+                Observable classification, Default to None.
+                By default launch analysis with an automatic classification.
+                (options: ``url, domain, hash, ip, generic``)
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+            IntelOwlClientException: on wrong observable_classification
+
+        Returns:
+            Dict: JSON body
+        """
+        try:
+            if not tags_labels:
+                tags_labels = []
+            if not runtime_configuration:
+                runtime_configuration = {}
+            if not observable_classification:
+                observable_classification = self._get_observable_classification(
+                    observable_name
+                )
+            elif observable_classification not in [
+                "generic",
+                "hash",
+                "ip",
+                "domain",
+                "url",
+            ]:
+                raise IntelOwlClientException(
+                    "Observable classification only handle"
+                    " 'generic', 'hash', 'ip', 'domain' and 'url' "
+                )
+            data = {
+                "observables": [[observable_classification, observable_name]],
+                "playbook_requested": playbook_requested,
+                "tags_labels": tags_labels,
+                "runtime_configuration": runtime_configuration,
+            }
+            # send this value only if populated,
+            # otherwise the backend would give you 400
+            if tlp:
+                data["tlp"] = tlp
+            answer = self.__send_analysis_request(
+                data=data, files=None, playbook_mode=True
+            )
+        except Exception as e:
+            raise IntelOwlClientException(e)
+        return answer
+
+    def send_analysis_batch(self, rows: List[Dict]):
+        """
+        Send multiple analysis requests.
+        Can be mix of observable or file analysis requests.
+
+        Used by the pyintelowl CLI.
+
+        Args:
+            rows (List[Dict]):
+                Each row should be a dictionary with keys,
+                `value`, `type`, `check`, `tlp`,
+                `analyzers_list`, `connectors_list`, `runtime_config`
+                `tags_list`.
+        """
+        for obj in rows:
+            try:
+                runtime_config = obj.get("runtime_config", {})
+                if runtime_config:
+                    with open(runtime_config) as fp:
+                        runtime_config = json.load(fp)
+
+                analyzers_list = obj.get("analyzers_list", [])
+                connectors_list = obj.get("connectors_list", [])
+                if isinstance(analyzers_list, str):
+                    analyzers_list = analyzers_list.split(",")
+                if isinstance(connectors_list, str):
+                    connectors_list = connectors_list.split(",")
+
+                self._new_analysis_cli(
+                    obj["value"],
+                    obj["type"],
+                    obj.get("check", None),
+                    obj.get("tlp", "WHITE"),
+                    analyzers_list,
+                    connectors_list,
+                    runtime_config,
+                    obj.get("tags_list", []),
+                    obj.get("should_poll", False),
+                )
+            except IntelOwlClientException as e:
+                self.logger.fatal(str(e))
+
+    def __send_analysis_request(self, data=None, files=None, playbook_mode=False):
+        """
+        Internal use only.
+        """
+        response = None
+        answer = {}
+        if files is None:
+            url = self.instance + "/api/analyze_observable"
+            if playbook_mode:
+                url = self.instance + "/api/playbook/analyze_multiple_observables"
+            args = {"json": data}
+        else:
+            url = self.instance + "/api/analyze_file"
+            if playbook_mode:
+                url = self.instance + "/api/playbook/analyze_multiple_files"
+            args = {"data": data, "files": files}
+        try:
+            response = self.session.post(url, **args)
+            self.logger.debug(
+                msg={
+                    "url": response.url,
+                    "code": response.status_code,
+                    "request": response.request.headers,
+                    "headers": response.headers,
+                    "body": response.json(),
+                }
+            )
+            answer = response.json()
+            if playbook_mode:
+                # right now, we are only supporting single input result
+                answers = answer.get("results", [])
+                if answers:
+                    answer = answers[0]
+
+            warnings = answer.get("warnings", [])
+            errors = answer.get("errors", {})
+            if self.cli:
+                info_log = f"""New Job running..
+                    ID: {answer.get('job_id')} | 
+                    Status: [u blue]{answer.get('status')}[/].
+                    Got {len(warnings)} warnings:
+                    [i yellow]{warnings if warnings else None}[/]
+                    Got {len(errors)} errors:
+                    [i red]{errors if errors else None}[/]
+                """
+            else:
+                info_log = (
+                    f"New Job running.. ID: {answer.get('job_id')} "
+                    f"| Status: {answer.get('status')}."
+                    f" Got {len(warnings)} warnings:"
+                    f" {warnings if warnings else None}"
+                    f" Got {len(errors)} errors:"
+                    f" {errors if errors else None}"
+                )
+            self.logger.info(info_log)
+            response.raise_for_status()
+        except Exception as e:
+            raise IntelOwlClientException(e, response=response)
+        return answer
+
+    def create_tag(self, label: str, color: str):
+        """Creates new tag by sending a POST Request
+        Endpoint: ``/api/tags``
+
+        Args:
+            label ([str]): [Label of the tag to be created]
+            color ([str]): [Color of the tag to be created]
+        """
+        url = self.instance + "/api/tags"
+        data = {"label": label, "color": color}
+        response = self.__make_request("POST", url=url, data=data)
+        return response.json()
+
+    def edit_tag(self, tag_id: Union[int, str], label: str, color: str):
+        """Edits existing tag by sending PUT request
+        Endpoint: ``api/tags``
+
+        Args:
+            id ([int]): [Id of the existing tag]
+            label ([str]): [Label of the tag to be created]
+            color ([str]): [Color of the tag to be created]
+        """
+        url = self.instance + "/api/tags/" + str(tag_id)
+        data = {"label": label, "color": color}
+        response = self.__make_request("PUT", url=url, data=data)
+        return response.json()
+
+    def get_all_tags(self) -> List[Dict[str, str]]:
+        """
+        Fetch list of all tags.\n
+        Endpoint: ``/api/tags``
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            List[Dict[str, str]]: List of tags
+        """
+        url = self.instance + "/api/tags"
+        response = self.__make_request("GET", url=url)
+        return response.json()
+
+    def get_all_jobs(self) -> List[Dict[str, Any]]:
+        """
+        Fetch list of all jobs.\n
+        Endpoint: ``/api/jobs``
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Dict: Dict with 3 keys: "count", "total_pages", "results"
+        """
+        url = self.instance + "/api/jobs"
+        response = self.__make_request("GET", url=url)
+        return response.json()
+
+    def get_tag_by_id(self, tag_id: Union[int, str]) -> Dict[str, str]:
+        """Fetch tag info by ID.\n
+        Endpoint: ``/api/tag/{tag_id}``
+
+        Args:
+            tag_id (Union[int, str]): Tag ID
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Dict[str, str]: Dict with 3 keys: `id`, `label` and `color`.
+        """
+
+        url = self.instance + "/api/tags/" + str(tag_id)
+        response = self.__make_request("GET", url=url)
+        return response.json()
+
+    def get_job_by_id(self, job_id: Union[int, str]) -> Dict[str, Any]:
+        """Fetch job info by ID.
+        Endpoint: ``/api/jobs/{job_id}``
+
+        Args:
+            job_id (Union[int, str]): Job ID
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Dict[str, Any]: JSON body.
+        """
+        url = self.instance + "/api/jobs/" + str(job_id)
+        response = self.__make_request("GET", url=url)
+        return response.json()
+
+    @staticmethod
+    def get_md5(
+        to_hash: AnyStr,
+        type_="observable",
+    ) -> str:
+        """Returns md5sum of given observable or file object.
+
+        Args:
+            to_hash (AnyStr):
+                either an observable string, file contents as bytes or path to a file
+            type_ (Union["observable", "binary", "file"], optional):
+                `observable`, `binary`, `file`. Defaults to "observable".
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            str: md5sum
+        """
+        md5 = ""
+        if type_ == "observable":
+            md5 = hashlib.md5(str(to_hash).lower().encode("utf-8")).hexdigest()
+        elif type_ == "binary":
+            md5 = hashlib.md5(to_hash).hexdigest()
+        elif type_ == "file":
+            path = pathlib.Path(to_hash)
+            if not path.exists():
+                raise IntelOwlClientException(f"{to_hash} does not exists")
+            binary = path.read_bytes()
+            md5 = hashlib.md5(binary).hexdigest()
+        return md5
+
+    def _new_analysis_cli(
+        self,
+        obj: str,
+        type_: str,
+        check,
+        tlp: TLPType = None,
+        analyzers_list: List[str] = None,
+        connectors_list: List[str] = None,
+        runtime_configuration: Dict = None,
+        tags_labels: List[str] = None,
+        should_poll: bool = False,
+        minutes_ago: int = None,
+    ) -> None:
+        """
+        For internal use by the pyintelowl CLI.
+        """
+        if not analyzers_list:
+            analyzers_list = []
+        if not connectors_list:
+            connectors_list = []
+        if not runtime_configuration:
+            runtime_configuration = {}
+        if not tags_labels:
+            tags_labels = []
+        self.logger.info(
+            f"""Requesting analysis..
+            {type_}: [blue]{obj}[/]
+            analyzers: [i green]{analyzers_list if analyzers_list else 'none'}[/]
+            connectors: [i green]{connectors_list if connectors_list else 'none'}[/]
+            tags: [i green]{tags_labels}[/]
+            """
+        )
+        # 1st step: ask analysis availability
+        if check != "force-new":
+            md5 = self.get_md5(obj, type_=type_)
+
+            resp = self.ask_analysis_availability(
+                md5,
+                analyzers_list,
+                True if check == "reported" else False,
+                minutes_ago,
+            )
+            status, job_id = resp.get("status", None), resp.get("job_id", None)
+            if status != "not_available":
+                self.logger.info(
+                    f"""Found existing analysis!
+                Job: #{job_id}
+                status: [u blue]{status}[/]
+
+                [i]Hint: use [#854442]--check force-new[/] to perform new scan anyway[/]
+                    """
+                )
+                return
+        # 2nd step: send new analysis request
+        if type_ == "observable":
+            resp2 = self.send_observable_analysis_request(
+                observable_name=obj,
+                tlp=tlp,
+                analyzers_requested=analyzers_list,
+                connectors_requested=connectors_list,
+                runtime_configuration=runtime_configuration,
+                tags_labels=tags_labels,
+            )
+        else:
+            path = pathlib.Path(obj)
+            resp2 = self.send_file_analysis_request(
+                filename=path.name,
+                binary=path.read_bytes(),
+                tlp=tlp,
+                analyzers_requested=analyzers_list,
+                connectors_requested=connectors_list,
+                runtime_configuration=runtime_configuration,
+                tags_labels=tags_labels,
+            )
+        # 3rd step: poll for result
+        if should_poll:
+            if resp2["status"] != "accepted":
+                self.logger.fatal("Can't poll a failed job")
+            # import poll function
+            from .cli._jobs_utils import _poll_for_job_cli
+
+            job_id = resp2["job_id"]
+            _ = _poll_for_job_cli(self, job_id)
+            self.logger.info(
+                f"""
+        Polling finished.
+        Execute [i blue]pyintelowl jobs view {job_id}[/] to view the result
+                """
+            )
+
+    def _new_analysis_playbook_cli(
+        self,
+        obj: str,
+        type_: str,
+        playbook: str,
+        tlp: TLPType = None,
+        runtime_configuration: Dict = None,
+        tags_labels: List[str] = None,
+        should_poll: bool = False,
+    ) -> None:
+        """
+        For internal use by the pyintelowl CLI.
+        """
+        if not runtime_configuration:
+            runtime_configuration = {}
+        if not tags_labels:
+            tags_labels = []
+
+        self.logger.info(
+            f"""Requesting analysis..
+            {type_}: [blue]{obj}[/]
+            playbook: [i green]{playbook}[/]
+            tags: [i green]{tags_labels}[/]
+            """
+        )
+
+        # 1st step, make request
+        if type_ == "observable":
+            resp = self.send_observable_analysis_playbook_request(
+                observable_name=obj,
+                playbook_requested=playbook,
+                tlp=tlp,
+                runtime_configuration=runtime_configuration,
+                tags_labels=tags_labels,
+            )
+        else:
+            path = pathlib.Path(obj)
+            resp = self.send_file_analysis_playbook_request(
+                filename=path.name,
+                binary=path.read_bytes(),
+                playbook_requested=playbook,
+                tlp=tlp,
+                runtime_configuration=runtime_configuration,
+                tags_labels=tags_labels,
+            )
+
+        # 2nd step: poll for result
+        if should_poll:
+            if resp.get("status", "") != "accepted":
+                self.logger.fatal("Can't poll a failed job")
+            # import poll function
+            from .cli._jobs_utils import _poll_for_job_cli
+
+            job_id = resp.get("job_id", 0)
+            _ = _poll_for_job_cli(self, job_id)
+            self.logger.info(
+                f"""
+                    Polling finished.
+                    Execute [i blue]pyintelowl jobs view {job_id}[/] to view the result
+                """
+            )
+
+    def _get_observable_classification(self, value: str) -> str:
+        """Returns observable classification for the given value.\n
+        Only following types are supported:
+        ip, domain, url, hash (md5, sha1, sha256), generic (if no match)
+
+        Args:
+            value (str):
+                observable value
+
+        Raises:
+            IntelOwlClientException:
+                if value type is not recognized
+
+        Returns:
+            str: one of `ip`, `url`, `domain`, `hash` or 'generic'.
+        """
+        try:
+            ipaddress.ip_address(value)
+        except ValueError:
+            if re.match(
+                r"^(?:htt|ft|tc)ps?://[a-z\d-]{1,63}(?:\.[a-z\d-]{1,63})+"
+                r"(?:/[a-z\d-]{1,63})*(?:\.\w+)?",
+                value,
+            ):
+                classification = "url"
+            elif re.match(r"^(\.)?[a-z\d-]{1,63}(\.[a-z\d-]{1,63})+$", value):
+                classification = "domain"
+            elif (
+                re.match(r"^[a-f\d]{32}$", value)
+                or re.match(r"^[a-f\d]{40}$", value)
+                or re.match(r"^[a-f\d]{64}$", value)
+                or re.match(r"^[A-F\d]{32}$", value)
+                or re.match(r"^[A-F\d]{40}$", value)
+                or re.match(r"^[A-F\d]{64}$", value)
+            ):
+                classification = "hash"
+            else:
+                classification = "generic"
+                self.logger.warning(
+                    "Couldn't detect observable classification, setting as 'generic'..."
+                )
+        else:
+            # its a simple IP
+            classification = "ip"
+
+        return classification
+
+    def download_sample(self, job_id: int) -> bytes:
+        """
+        Download file sample from job.\n
+        Method: GET
+        Endpoint: ``/api/jobs/{job_id}/download_sample``
+
+        Args:
+            job_id (int):
+                id of job to download sample from
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bytes: Raw file data.
+        """
+
+        url = self.instance + f"/api/jobs/{job_id}/download_sample"
+        response = self.__make_request("GET", url=url)
+        return response.content
+
+    def kill_running_job(self, job_id: int) -> bool:
+        """Send kill_running_job request.\n
+        Method: PATCH
+        Endpoint: ``/api/jobs/{job_id}/kill``
+
+        Args:
+            job_id (int):
+                id of job to kill
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: killed or not
+        """
+
+        url = self.instance + f"/api/jobs/{job_id}/kill"
+        response = self.__make_request("PATCH", url=url)
+        killed = response.status_code == 204
+        return killed
+
+    def delete_job_by_id(self, job_id: int) -> bool:
+        """Send delete job request.\n
+        Method: DELETE
+        Endpoint: ``/api/jobs/{job_id}``
+
+        Args:
+            job_id (int):
+                id of job to kill
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: deleted or not
+        """
+        url = self.instance + "/api/jobs/" + str(job_id)
+        response = self.__make_request("DELETE", url=url)
+        deleted = response.status_code == 204
+        return deleted
+
+    def delete_tag_by_id(self, tag_id: int) -> bool:
+        """Send delete tag request.\n
+        Method: DELETE
+        Endpoint: ``/api/tags/{tag_id}``
+
+        Args:
+            tag_id (int):
+                id of tag to delete
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: deleted or not
+        """
+
+        url = self.instance + "/api/tags/" + str(tag_id)
+        response = self.__make_request("DELETE", url=url)
+        deleted = response.status_code == 204
+        return deleted
+
+    def __run_plugin_action(
+        self, job_id: int, plugin_type: str, plugin_name: str, plugin_action: str
+    ) -> bool:
+        """Internal method for kill/retry for analyzer/connector"""
+        response = None
+        url = (
+            self.instance
+            + f"/api/jobs/{job_id}/{plugin_type}/{plugin_name}/{plugin_action}"
+        )
+        response = self.__make_request("PATCH", url=url)
+        success = response.status_code == 204
+        return success
+
+    def kill_analyzer(self, job_id: int, analyzer_name: str) -> bool:
+        """Send kill running/pending analyzer request.\n
+        Method: PATCH
+        Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/kill``
+
+        Args:
+            job_id (int):
+                id of job
+            analyzer_name (str):
+                name of analyzer to kill
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: killed or not
+        """
+
+        killed = self.__run_plugin_action(
+            job_id=job_id,
+            plugin_name=analyzer_name,
+            plugin_type="analyzer",
+            plugin_action="kill",
+        )
+        return killed
+
+    def kill_connector(self, job_id: int, connector_name: str) -> bool:
+        """Send kill running/pending connector request.\n
+        Method: PATCH
+        Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/kill``
+
+        Args:
+            job_id (int):
+                id of job
+            connector_name (str):
+                name of connector to kill
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: killed or not
+        """
+
+        killed = self.__run_plugin_action(
+            job_id=job_id,
+            plugin_name=connector_name,
+            plugin_type="connector",
+            plugin_action="kill",
+        )
+        return killed
+
+    def retry_analyzer(self, job_id: int, analyzer_name: str) -> bool:
+        """Send retry failed/killed analyzer request.\n
+        Method: PATCH
+        Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/retry``
+
+        Args:
+            job_id (int):
+                id of job
+            analyzer_name (str):
+                name of analyzer to retry
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: success or not
+        """
+
+        success = self.__run_plugin_action(
+            job_id=job_id,
+            plugin_name=analyzer_name,
+            plugin_type="analyzer",
+            plugin_action="retry",
+        )
+        return success
+
+    def retry_connector(self, job_id: int, connector_name: str) -> bool:
+        """Send retry failed/killed connector request.\n
+        Method: PATCH
+        Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/retry``
+
+        Args:
+            job_id (int):
+                id of job
+            connector_name (str):
+                name of connector to retry
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: success or not
+        """
+
+        success = self.__run_plugin_action(
+            job_id=job_id,
+            plugin_name=connector_name,
+            plugin_type="connector",
+            plugin_action="retry",
+        )
+        return success
+
+    def analyzer_healthcheck(self, analyzer_name: str) -> Optional[bool]:
+        """Send analyzer(docker-based) health check request.\n
+        Method: GET
+        Endpoint: ``/api/analyzer/{analyzer_name}/healthcheck``
+
+        Args:
+            analyzer_name (str):
+                name of analyzer
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: success or not
+        """
+
+        url = self.instance + f"/api/analyzer/{analyzer_name}/healthcheck"
+        response = self.__make_request("GET", url=url)
+        return response.json().get("status", None)
+
+    def connector_healthcheck(self, connector_name: str) -> Optional[bool]:
+        """Send connector health check request.\n
+        Method: GET
+        Endpoint: ``/api/connector/{connector_name}/healthcheck``
+
+        Args:
+            connector_name (str):
+                name of connector
+
+        Raises:
+            IntelOwlClientException: on client/HTTP error
+
+        Returns:
+            Bool: success or not
+        """
+        url = self.instance + f"/api/connector/{connector_name}/healthcheck"
+        response = self.__make_request("GET", url=url)
+        return response.json().get("status", None)
+
+
+
+
+

+session: requests.Session + +property + +

+
+

Internal use only.

+
+
+
+

+__make_request(method='GET', *args, **kwargs) +

+
+

For internal use only.

+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
def __make_request(
+    self,
+    method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"] = "GET",
+    *args,
+    **kwargs,
+) -> requests.Response:
+    """
+    For internal use only.
+    """
+    response: requests.Response = None
+    requests_function_map: Dict[str, Callable] = {
+        "GET": self.session.get,
+        "POST": self.session.post,
+        "PUT": self.session.put,
+        "PATCH": self.session.patch,
+        "DELETE": self.session.delete,
+    }
+    func = requests_function_map.get(method, None)
+    if not func:
+        raise RuntimeError(f"Unsupported method name: {method}")
+
+    try:
+        response = func(*args, **kwargs)
+        self.logger.debug(
+            msg=(response.url, response.status_code, response.content)
+        )
+        response.raise_for_status()
+    except Exception as e:
+        raise IntelOwlClientException(e, response=response)
+
+    return response
+
+
+
+
+
+

+__run_plugin_action(job_id, plugin_type, plugin_name, plugin_action) +

+
+

Internal method for kill/retry for analyzer/connector

+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def __run_plugin_action(
+    self, job_id: int, plugin_type: str, plugin_name: str, plugin_action: str
+) -> bool:
+    """Internal method for kill/retry for analyzer/connector"""
+    response = None
+    url = (
+        self.instance
+        + f"/api/jobs/{job_id}/{plugin_type}/{plugin_name}/{plugin_action}"
+    )
+    response = self.__make_request("PATCH", url=url)
+    success = response.status_code == 204
+    return success
+
+
+
+
+
+

+__send_analysis_request(data=None, files=None, playbook_mode=False) +

+
+

Internal use only.

+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def __send_analysis_request(self, data=None, files=None, playbook_mode=False):
+    """
+    Internal use only.
+    """
+    response = None
+    answer = {}
+    if files is None:
+        url = self.instance + "/api/analyze_observable"
+        if playbook_mode:
+            url = self.instance + "/api/playbook/analyze_multiple_observables"
+        args = {"json": data}
+    else:
+        url = self.instance + "/api/analyze_file"
+        if playbook_mode:
+            url = self.instance + "/api/playbook/analyze_multiple_files"
+        args = {"data": data, "files": files}
+    try:
+        response = self.session.post(url, **args)
+        self.logger.debug(
+            msg={
+                "url": response.url,
+                "code": response.status_code,
+                "request": response.request.headers,
+                "headers": response.headers,
+                "body": response.json(),
+            }
+        )
+        answer = response.json()
+        if playbook_mode:
+            # right now, we are only supporting single input result
+            answers = answer.get("results", [])
+            if answers:
+                answer = answers[0]
+
+        warnings = answer.get("warnings", [])
+        errors = answer.get("errors", {})
+        if self.cli:
+            info_log = f"""New Job running..
+                ID: {answer.get('job_id')} | 
+                Status: [u blue]{answer.get('status')}[/].
+                Got {len(warnings)} warnings:
+                [i yellow]{warnings if warnings else None}[/]
+                Got {len(errors)} errors:
+                [i red]{errors if errors else None}[/]
+            """
+        else:
+            info_log = (
+                f"New Job running.. ID: {answer.get('job_id')} "
+                f"| Status: {answer.get('status')}."
+                f" Got {len(warnings)} warnings:"
+                f" {warnings if warnings else None}"
+                f" Got {len(errors)} errors:"
+                f" {errors if errors else None}"
+            )
+        self.logger.info(info_log)
+        response.raise_for_status()
+    except Exception as e:
+        raise IntelOwlClientException(e, response=response)
+    return answer
+
+
+
+
+
+

+analyzer_healthcheck(analyzer_name) +

+
+

Send analyzer(docker-based) health check request.

+

Method: GET +Endpoint: /api/analyzer/{analyzer_name}/healthcheck

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
analyzer_name +str + +
+

name of analyzer

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +Optional[bool] + +
+

success or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def analyzer_healthcheck(self, analyzer_name: str) -> Optional[bool]:
+    """Send analyzer(docker-based) health check request.\n
+    Method: GET
+    Endpoint: ``/api/analyzer/{analyzer_name}/healthcheck``
+
+    Args:
+        analyzer_name (str):
+            name of analyzer
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: success or not
+    """
+
+    url = self.instance + f"/api/analyzer/{analyzer_name}/healthcheck"
+    response = self.__make_request("GET", url=url)
+    return response.json().get("status", None)
+
+
+
+
+
+

+ask_analysis_availability(md5, analyzers=None, check_reported_analysis_too=False, minutes_ago=None) +

+
+

Search for already available analysis.

+

Endpoint: /api/ask_analysis_availability

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
md5 +str + +
+

md5sum of the observable or file

+
+
+required +
analyzers +List[str] + +
+
+
+None +
check_reported_analysis_too +bool + +
+
+
+False +
minutes_ago +int + +
+
+
+None +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Dict +Dict + +
+

JSON body

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def ask_analysis_availability(
+    self,
+    md5: str,
+    analyzers: List[str] = None,
+    check_reported_analysis_too: bool = False,
+    minutes_ago: int = None,
+) -> Dict:
+    """Search for already available analysis.\n
+    Endpoint: ``/api/ask_analysis_availability``
+
+    Args:
+        md5 (str): md5sum of the observable or file
+        analyzers (List[str], optional):
+        list of analyzers to trigger.
+        Defaults to `None` meaning automatically select all configured analyzers.
+        check_reported_analysis_too (bool, optional):
+        Check against all existing jobs. Defaults to ``False``.
+        minutes_ago (int, optional):
+        number of minutes to check back for analysis.
+        Default is None so the check does not have any time limits.
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Dict: JSON body
+    """
+    if not analyzers:
+        analyzers = []
+    data = {"md5": md5, "analyzers": analyzers}
+    if not check_reported_analysis_too:
+        data["running_only"] = True
+    if minutes_ago:
+        data["minutes_ago"] = int(minutes_ago)
+    url = self.instance + "/api/ask_analysis_availability"
+    response = self.__make_request("POST", url=url, data=data)
+    answer = response.json()
+    status, job_id = answer.get("status", None), answer.get("job_id", None)
+    # check sanity cases
+    if not status:
+        raise IntelOwlClientException(
+            "API ask_analysis_availability gave result without status ?"
+            f" Response: {answer}"
+        )
+    if status != "not_available" and not job_id:
+        raise IntelOwlClientException(
+            "API ask_analysis_availability gave result without job_id ?"
+            f" Response: {answer}"
+        )
+    return answer
+
+
+
+
+
+

+connector_healthcheck(connector_name) +

+
+

Send connector health check request.

+

Method: GET +Endpoint: /api/connector/{connector_name}/healthcheck

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
connector_name +str + +
+

name of connector

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +Optional[bool] + +
+

success or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def connector_healthcheck(self, connector_name: str) -> Optional[bool]:
+    """Send connector health check request.\n
+    Method: GET
+    Endpoint: ``/api/connector/{connector_name}/healthcheck``
+
+    Args:
+        connector_name (str):
+            name of connector
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: success or not
+    """
+    url = self.instance + f"/api/connector/{connector_name}/healthcheck"
+    response = self.__make_request("GET", url=url)
+    return response.json().get("status", None)
+
+
+
+
+
+

+create_tag(label, color) +

+
+

Creates new tag by sending a POST Request +Endpoint: /api/tags

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
label +[str] + +
+

[Label of the tag to be created]

+
+
+required +
color +[str] + +
+

[Color of the tag to be created]

+
+
+required +
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def create_tag(self, label: str, color: str):
+    """Creates new tag by sending a POST Request
+    Endpoint: ``/api/tags``
+
+    Args:
+        label ([str]): [Label of the tag to be created]
+        color ([str]): [Color of the tag to be created]
+    """
+    url = self.instance + "/api/tags"
+    data = {"label": label, "color": color}
+    response = self.__make_request("POST", url=url, data=data)
+    return response.json()
+
+
+
+
+
+

+delete_job_by_id(job_id) +

+
+

Send delete job request.

+

Method: DELETE +Endpoint: /api/jobs/{job_id}

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job to kill

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

deleted or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def delete_job_by_id(self, job_id: int) -> bool:
+    """Send delete job request.\n
+    Method: DELETE
+    Endpoint: ``/api/jobs/{job_id}``
+
+    Args:
+        job_id (int):
+            id of job to kill
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: deleted or not
+    """
+    url = self.instance + "/api/jobs/" + str(job_id)
+    response = self.__make_request("DELETE", url=url)
+    deleted = response.status_code == 204
+    return deleted
+
+
+
+
+
+

+delete_tag_by_id(tag_id) +

+
+

Send delete tag request.

+

Method: DELETE +Endpoint: /api/tags/{tag_id}

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tag_id +int + +
+

id of tag to delete

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

deleted or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def delete_tag_by_id(self, tag_id: int) -> bool:
+    """Send delete tag request.\n
+    Method: DELETE
+    Endpoint: ``/api/tags/{tag_id}``
+
+    Args:
+        tag_id (int):
+            id of tag to delete
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: deleted or not
+    """
+
+    url = self.instance + "/api/tags/" + str(tag_id)
+    response = self.__make_request("DELETE", url=url)
+    deleted = response.status_code == 204
+    return deleted
+
+
+
+
+
+

+download_sample(job_id) +

+
+

Download file sample from job.

+

Method: GET +Endpoint: /api/jobs/{job_id}/download_sample

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job to download sample from

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bytes +bytes + +
+

Raw file data.

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def download_sample(self, job_id: int) -> bytes:
+    """
+    Download file sample from job.\n
+    Method: GET
+    Endpoint: ``/api/jobs/{job_id}/download_sample``
+
+    Args:
+        job_id (int):
+            id of job to download sample from
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bytes: Raw file data.
+    """
+
+    url = self.instance + f"/api/jobs/{job_id}/download_sample"
+    response = self.__make_request("GET", url=url)
+    return response.content
+
+
+
+
+
+

+edit_tag(tag_id, label, color) +

+
+

Edits existing tag by sending PUT request +Endpoint: api/tags

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
id +[int] + +
+

[Id of the existing tag]

+
+
+required +
label +[str] + +
+

[Label of the tag to be created]

+
+
+required +
color +[str] + +
+

[Color of the tag to be created]

+
+
+required +
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def edit_tag(self, tag_id: Union[int, str], label: str, color: str):
+    """Edits existing tag by sending PUT request
+    Endpoint: ``api/tags``
+
+    Args:
+        id ([int]): [Id of the existing tag]
+        label ([str]): [Label of the tag to be created]
+        color ([str]): [Color of the tag to be created]
+    """
+    url = self.instance + "/api/tags/" + str(tag_id)
+    data = {"label": label, "color": color}
+    response = self.__make_request("PUT", url=url, data=data)
+    return response.json()
+
+
+
+
+
+

+get_all_jobs() +

+
+

Fetch list of all jobs.

+

Endpoint: /api/jobs

+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Dict +List[Dict[str, Any]] + +
+

Dict with 3 keys: "count", "total_pages", "results"

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def get_all_jobs(self) -> List[Dict[str, Any]]:
+    """
+    Fetch list of all jobs.\n
+    Endpoint: ``/api/jobs``
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Dict: Dict with 3 keys: "count", "total_pages", "results"
+    """
+    url = self.instance + "/api/jobs"
+    response = self.__make_request("GET", url=url)
+    return response.json()
+
+
+
+
+
+

+get_all_tags() +

+
+

Fetch list of all tags.

+

Endpoint: /api/tags

+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+List[Dict[str, str]] + +
+

List[Dict[str, str]]: List of tags

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def get_all_tags(self) -> List[Dict[str, str]]:
+    """
+    Fetch list of all tags.\n
+    Endpoint: ``/api/tags``
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        List[Dict[str, str]]: List of tags
+    """
+    url = self.instance + "/api/tags"
+    response = self.__make_request("GET", url=url)
+    return response.json()
+
+
+
+
+
+

+get_job_by_id(job_id) +

+
+

Fetch job info by ID. +Endpoint: /api/jobs/{job_id}

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +Union[int, str] + +
+

Job ID

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+Dict[str, Any] + +
+

Dict[str, Any]: JSON body.

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def get_job_by_id(self, job_id: Union[int, str]) -> Dict[str, Any]:
+    """Fetch job info by ID.
+    Endpoint: ``/api/jobs/{job_id}``
+
+    Args:
+        job_id (Union[int, str]): Job ID
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Dict[str, Any]: JSON body.
+    """
+    url = self.instance + "/api/jobs/" + str(job_id)
+    response = self.__make_request("GET", url=url)
+    return response.json()
+
+
+
+
+
+

+get_md5(to_hash, type_='observable') + +staticmethod + +

+
+

Returns md5sum of given observable or file object.

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
to_hash +AnyStr + +
+

either an observable string, file contents as bytes or path to a file

+
+
+required +
type_ +Union[observable, binary, file] + +
+

observable, binary, file. Defaults to "observable".

+
+
+'observable' +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
str +str + +
+

md5sum

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
@staticmethod
+def get_md5(
+    to_hash: AnyStr,
+    type_="observable",
+) -> str:
+    """Returns md5sum of given observable or file object.
+
+    Args:
+        to_hash (AnyStr):
+            either an observable string, file contents as bytes or path to a file
+        type_ (Union["observable", "binary", "file"], optional):
+            `observable`, `binary`, `file`. Defaults to "observable".
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        str: md5sum
+    """
+    md5 = ""
+    if type_ == "observable":
+        md5 = hashlib.md5(str(to_hash).lower().encode("utf-8")).hexdigest()
+    elif type_ == "binary":
+        md5 = hashlib.md5(to_hash).hexdigest()
+    elif type_ == "file":
+        path = pathlib.Path(to_hash)
+        if not path.exists():
+            raise IntelOwlClientException(f"{to_hash} does not exists")
+        binary = path.read_bytes()
+        md5 = hashlib.md5(binary).hexdigest()
+    return md5
+
+
+
+
+
+

+get_tag_by_id(tag_id) +

+
+

Fetch tag info by ID.

+

Endpoint: /api/tag/{tag_id}

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tag_id +Union[int, str] + +
+

Tag ID

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+Dict[str, str] + +
+

Dict[str, str]: Dict with 3 keys: id, label and color.

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def get_tag_by_id(self, tag_id: Union[int, str]) -> Dict[str, str]:
+    """Fetch tag info by ID.\n
+    Endpoint: ``/api/tag/{tag_id}``
+
+    Args:
+        tag_id (Union[int, str]): Tag ID
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Dict[str, str]: Dict with 3 keys: `id`, `label` and `color`.
+    """
+
+    url = self.instance + "/api/tags/" + str(tag_id)
+    response = self.__make_request("GET", url=url)
+    return response.json()
+
+
+
+
+
+

+kill_analyzer(job_id, analyzer_name) +

+
+

Send kill running/pending analyzer request.

+

Method: PATCH +Endpoint: /api/jobs/{job_id}/analyzer/{analyzer_name}/kill

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job

+
+
+required +
analyzer_name +str + +
+

name of analyzer to kill

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

killed or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def kill_analyzer(self, job_id: int, analyzer_name: str) -> bool:
+    """Send kill running/pending analyzer request.\n
+    Method: PATCH
+    Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/kill``
+
+    Args:
+        job_id (int):
+            id of job
+        analyzer_name (str):
+            name of analyzer to kill
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: killed or not
+    """
+
+    killed = self.__run_plugin_action(
+        job_id=job_id,
+        plugin_name=analyzer_name,
+        plugin_type="analyzer",
+        plugin_action="kill",
+    )
+    return killed
+
+
+
+
+
+

+kill_connector(job_id, connector_name) +

+
+

Send kill running/pending connector request.

+

Method: PATCH +Endpoint: /api/jobs/{job_id}/connector/{connector_name}/kill

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job

+
+
+required +
connector_name +str + +
+

name of connector to kill

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

killed or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def kill_connector(self, job_id: int, connector_name: str) -> bool:
+    """Send kill running/pending connector request.\n
+    Method: PATCH
+    Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/kill``
+
+    Args:
+        job_id (int):
+            id of job
+        connector_name (str):
+            name of connector to kill
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: killed or not
+    """
+
+    killed = self.__run_plugin_action(
+        job_id=job_id,
+        plugin_name=connector_name,
+        plugin_type="connector",
+        plugin_action="kill",
+    )
+    return killed
+
+
+
+
+
+

+kill_running_job(job_id) +

+
+

Send kill_running_job request.

+

Method: PATCH +Endpoint: /api/jobs/{job_id}/kill

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job to kill

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

killed or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def kill_running_job(self, job_id: int) -> bool:
+    """Send kill_running_job request.\n
+    Method: PATCH
+    Endpoint: ``/api/jobs/{job_id}/kill``
+
+    Args:
+        job_id (int):
+            id of job to kill
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: killed or not
+    """
+
+    url = self.instance + f"/api/jobs/{job_id}/kill"
+    response = self.__make_request("PATCH", url=url)
+    killed = response.status_code == 204
+    return killed
+
+
+
+
+
+

+retry_analyzer(job_id, analyzer_name) +

+
+

Send retry failed/killed analyzer request.

+

Method: PATCH +Endpoint: /api/jobs/{job_id}/analyzer/{analyzer_name}/retry

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job

+
+
+required +
analyzer_name +str + +
+

name of analyzer to retry

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

success or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def retry_analyzer(self, job_id: int, analyzer_name: str) -> bool:
+    """Send retry failed/killed analyzer request.\n
+    Method: PATCH
+    Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/retry``
+
+    Args:
+        job_id (int):
+            id of job
+        analyzer_name (str):
+            name of analyzer to retry
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: success or not
+    """
+
+    success = self.__run_plugin_action(
+        job_id=job_id,
+        plugin_name=analyzer_name,
+        plugin_type="analyzer",
+        plugin_action="retry",
+    )
+    return success
+
+
+
+
+
+

+retry_connector(job_id, connector_name) +

+
+

Send retry failed/killed connector request.

+

Method: PATCH +Endpoint: /api/jobs/{job_id}/connector/{connector_name}/retry

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
job_id +int + +
+

id of job

+
+
+required +
connector_name +str + +
+

name of connector to retry

+
+
+required +
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Bool +bool + +
+

success or not

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def retry_connector(self, job_id: int, connector_name: str) -> bool:
+    """Send retry failed/killed connector request.\n
+    Method: PATCH
+    Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/retry``
+
+    Args:
+        job_id (int):
+            id of job
+        connector_name (str):
+            name of connector to retry
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Bool: success or not
+    """
+
+    success = self.__run_plugin_action(
+        job_id=job_id,
+        plugin_name=connector_name,
+        plugin_type="connector",
+        plugin_action="retry",
+    )
+    return success
+
+
+
+
+
+

+send_analysis_batch(rows) +

+
+

Send multiple analysis requests. +Can be mix of observable or file analysis requests.

+

Used by the pyintelowl CLI.

+

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
rows +List[Dict] + +
+

Each row should be a dictionary with keys, +value, type, check, tlp, +analyzers_list, connectors_list, runtime_config +tags_list.

+
+
+required +
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def send_analysis_batch(self, rows: List[Dict]):
+    """
+    Send multiple analysis requests.
+    Can be mix of observable or file analysis requests.
+
+    Used by the pyintelowl CLI.
+
+    Args:
+        rows (List[Dict]):
+            Each row should be a dictionary with keys,
+            `value`, `type`, `check`, `tlp`,
+            `analyzers_list`, `connectors_list`, `runtime_config`
+            `tags_list`.
+    """
+    for obj in rows:
+        try:
+            runtime_config = obj.get("runtime_config", {})
+            if runtime_config:
+                with open(runtime_config) as fp:
+                    runtime_config = json.load(fp)
+
+            analyzers_list = obj.get("analyzers_list", [])
+            connectors_list = obj.get("connectors_list", [])
+            if isinstance(analyzers_list, str):
+                analyzers_list = analyzers_list.split(",")
+            if isinstance(connectors_list, str):
+                connectors_list = connectors_list.split(",")
+
+            self._new_analysis_cli(
+                obj["value"],
+                obj["type"],
+                obj.get("check", None),
+                obj.get("tlp", "WHITE"),
+                analyzers_list,
+                connectors_list,
+                runtime_config,
+                obj.get("tags_list", []),
+                obj.get("should_poll", False),
+            )
+        except IntelOwlClientException as e:
+            self.logger.fatal(str(e))
+
+
+
+
+
+

+send_file_analysis_playbook_request(filename, binary, playbook_requested, tlp='CLEAR', runtime_configuration=None, tags_labels=None) +

+
+

Send playbook analysis request for a file.

+

Endpoint: /api/playbook/analyze_multiple_files

+

Args:

+
filename (str):
+    Filename
+binary (bytes):
+    File contents as bytes
+playbook_requested (str, optional):
+tlp (str, optional):
+    TLP for the analysis.
+    (options: ``WHITE, GREEN, AMBER, RED``).
+runtime_configuration (Dict, optional):
+    Overwrite configuration for analyzers. Defaults to ``{}``.
+tags_labels (List[str], optional):
+    List of tag labels to assign (creates non-existing tags)
+
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Dict +Dict + +
+

JSON body

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def send_file_analysis_playbook_request(
+    self,
+    filename: str,
+    binary: bytes,
+    playbook_requested: str,
+    tlp: TLPType = "CLEAR",
+    runtime_configuration: Dict = None,
+    tags_labels: List[str] = None,
+) -> Dict:
+    """Send playbook analysis request for a file.\n
+    Endpoint: ``/api/playbook/analyze_multiple_files``
+
+    Args:
+
+        filename (str):
+            Filename
+        binary (bytes):
+            File contents as bytes
+        playbook_requested (str, optional):
+        tlp (str, optional):
+            TLP for the analysis.
+            (options: ``WHITE, GREEN, AMBER, RED``).
+        runtime_configuration (Dict, optional):
+            Overwrite configuration for analyzers. Defaults to ``{}``.
+        tags_labels (List[str], optional):
+            List of tag labels to assign (creates non-existing tags)
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Dict: JSON body
+    """
+    try:
+        if not tags_labels:
+            tags_labels = []
+        if not runtime_configuration:
+            runtime_configuration = {}
+        data = {
+            "playbook_requested": playbook_requested,
+            "tags_labels": tags_labels,
+        }
+        # send this value only if populated,
+        # otherwise the backend would give you 400
+        if tlp:
+            data["tlp"] = tlp
+
+        if runtime_configuration:
+            data["runtime_configuration"] = json.dumps(runtime_configuration)
+        # `files` is wanted to be different from the other
+        # /api/analyze_file endpoint
+        # because the server is using different serializers
+        files = {"files": (filename, binary)}
+        answer = self.__send_analysis_request(
+            data=data, files=files, playbook_mode=True
+        )
+    except Exception as e:
+        raise IntelOwlClientException(e)
+    return answer
+
+
+
+
+
+

+send_file_analysis_request(filename, binary, tlp='CLEAR', analyzers_requested=None, connectors_requested=None, runtime_configuration=None, tags_labels=None) +

+
+

Send analysis request for a file.

+

Endpoint: /api/analyze_file

+

Args:

+
filename (str):
+    Filename
+binary (bytes):
+    File contents as bytes
+analyzers_requested (List[str], optional):
+    List of analyzers to invoke
+    Defaults to ``[]`` i.e. all analyzers.
+connectors_requested (List[str], optional):
+    List of specific connectors to invoke.
+    Defaults to ``[]`` i.e. all connectors.
+tlp (str, optional):
+    TLP for the analysis.
+    (options: ``CLEAR, GREEN, AMBER, RED``).
+runtime_configuration (Dict, optional):
+    Overwrite configuration for analyzers. Defaults to ``{}``.
+tags_labels (List[str], optional):
+    List of tag labels to assign (creates non-existing tags)
+
+

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Dict +Dict + +
+

JSON body

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def send_file_analysis_request(
+    self,
+    filename: str,
+    binary: bytes,
+    tlp: TLPType = "CLEAR",
+    analyzers_requested: List[str] = None,
+    connectors_requested: List[str] = None,
+    runtime_configuration: Dict = None,
+    tags_labels: List[str] = None,
+) -> Dict:
+    """Send analysis request for a file.\n
+    Endpoint: ``/api/analyze_file``
+
+    Args:
+
+        filename (str):
+            Filename
+        binary (bytes):
+            File contents as bytes
+        analyzers_requested (List[str], optional):
+            List of analyzers to invoke
+            Defaults to ``[]`` i.e. all analyzers.
+        connectors_requested (List[str], optional):
+            List of specific connectors to invoke.
+            Defaults to ``[]`` i.e. all connectors.
+        tlp (str, optional):
+            TLP for the analysis.
+            (options: ``CLEAR, GREEN, AMBER, RED``).
+        runtime_configuration (Dict, optional):
+            Overwrite configuration for analyzers. Defaults to ``{}``.
+        tags_labels (List[str], optional):
+            List of tag labels to assign (creates non-existing tags)
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+
+    Returns:
+        Dict: JSON body
+    """
+    try:
+        if not tlp:
+            tlp = "CLEAR"
+        if not analyzers_requested:
+            analyzers_requested = []
+        if not connectors_requested:
+            connectors_requested = []
+        if not tags_labels:
+            tags_labels = []
+        if not runtime_configuration:
+            runtime_configuration = {}
+        data = {
+            "file_name": filename,
+            "analyzers_requested": analyzers_requested,
+            "connectors_requested": connectors_requested,
+            "tlp": tlp,
+            "tags_labels": tags_labels,
+        }
+        if runtime_configuration:
+            data["runtime_configuration"] = json.dumps(runtime_configuration)
+        files = {"file": (filename, binary)}
+        answer = self.__send_analysis_request(data=data, files=files)
+    except Exception as e:
+        raise IntelOwlClientException(e)
+    return answer
+
+
+
+
+
+

+send_observable_analysis_playbook_request(observable_name, playbook_requested, tlp='CLEAR', runtime_configuration=None, tags_labels=None, observable_classification=None) +

+
+

Send playbook analysis request for an observable.

+

Endpoint: /api/playbook/analyze_multiple_observables

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
observable_name +str + +
+

Observable value

+
+
+required +
playbook_requested +str + +
+
+
+required +
tlp +str + +
+

TLP for the analysis. +(options: WHITE, GREEN, AMBER, RED).

+
+
+'CLEAR' +
runtime_configuration +Dict + +
+

Overwrite configuration for analyzers. Defaults to {}.

+
+
+None +
tags_labels +List[str] + +
+

List of tag labels to assign (creates non-existing tags)

+
+
+None +
observable_classification +str + +
+

Observable classification, Default to None. +By default launch analysis with an automatic classification. +(options: url, domain, hash, ip, generic)

+
+
+None +
+

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+IntelOwlClientException + +
+

on wrong observable_classification

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Dict +Dict + +
+

JSON body

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def send_observable_analysis_playbook_request(
+    self,
+    observable_name: str,
+    playbook_requested: str,
+    tlp: TLPType = "CLEAR",
+    runtime_configuration: Dict = None,
+    tags_labels: List[str] = None,
+    observable_classification: str = None,
+) -> Dict:
+    """Send playbook analysis request for an observable.\n
+    Endpoint: ``/api/playbook/analyze_multiple_observables``
+
+    Args:
+        observable_name (str):
+            Observable value
+        playbook_requested str:
+        tlp (str, optional):
+            TLP for the analysis.
+            (options: ``WHITE, GREEN, AMBER, RED``).
+        runtime_configuration (Dict, optional):
+            Overwrite configuration for analyzers. Defaults to ``{}``.
+        tags_labels (List[str], optional):
+            List of tag labels to assign (creates non-existing tags)
+        observable_classification (str):
+            Observable classification, Default to None.
+            By default launch analysis with an automatic classification.
+            (options: ``url, domain, hash, ip, generic``)
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+        IntelOwlClientException: on wrong observable_classification
+
+    Returns:
+        Dict: JSON body
+    """
+    try:
+        if not tags_labels:
+            tags_labels = []
+        if not runtime_configuration:
+            runtime_configuration = {}
+        if not observable_classification:
+            observable_classification = self._get_observable_classification(
+                observable_name
+            )
+        elif observable_classification not in [
+            "generic",
+            "hash",
+            "ip",
+            "domain",
+            "url",
+        ]:
+            raise IntelOwlClientException(
+                "Observable classification only handle"
+                " 'generic', 'hash', 'ip', 'domain' and 'url' "
+            )
+        data = {
+            "observables": [[observable_classification, observable_name]],
+            "playbook_requested": playbook_requested,
+            "tags_labels": tags_labels,
+            "runtime_configuration": runtime_configuration,
+        }
+        # send this value only if populated,
+        # otherwise the backend would give you 400
+        if tlp:
+            data["tlp"] = tlp
+        answer = self.__send_analysis_request(
+            data=data, files=None, playbook_mode=True
+        )
+    except Exception as e:
+        raise IntelOwlClientException(e)
+    return answer
+
+
+
+
+
+

+send_observable_analysis_request(observable_name, tlp='CLEAR', analyzers_requested=None, connectors_requested=None, runtime_configuration=None, tags_labels=None, observable_classification=None) +

+
+

Send analysis request for an observable.

+

Endpoint: /api/analyze_observable

+

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
observable_name +str + +
+

Observable value

+
+
+required +
analyzers_requested +List[str] + +
+

List of analyzers to invoke +Defaults to [] i.e. all analyzers.

+
+
+None +
connectors_requested +List[str] + +
+

List of specific connectors to invoke. +Defaults to [] i.e. all connectors.

+
+
+None +
tlp +str + +
+

TLP for the analysis. +(options: CLEAR, GREEN, AMBER, RED).

+
+
+'CLEAR' +
runtime_configuration +Dict + +
+

Overwrite configuration for analyzers. Defaults to {}.

+
+
+None +
tags_labels +List[str] + +
+

List of tag labels to assign (creates non-existing tags)

+
+
+None +
observable_classification +str + +
+

Observable classification, Default to None. +By default launch analysis with an automatic classification. +(options: url, domain, hash, ip, generic)

+
+
+None +
+

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+IntelOwlClientException + +
+

on client/HTTP error

+
+
+IntelOwlClientException + +
+

on wrong observable_classification

+
+
+

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Dict +Dict + +
+

JSON body

+
+
+
+Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py +
def send_observable_analysis_request(
+    self,
+    observable_name: str,
+    tlp: TLPType = "CLEAR",
+    analyzers_requested: List[str] = None,
+    connectors_requested: List[str] = None,
+    runtime_configuration: Dict = None,
+    tags_labels: List[str] = None,
+    observable_classification: str = None,
+) -> Dict:
+    """Send analysis request for an observable.\n
+    Endpoint: ``/api/analyze_observable``
+
+    Args:
+        observable_name (str):
+            Observable value
+        analyzers_requested (List[str], optional):
+            List of analyzers to invoke
+            Defaults to ``[]`` i.e. all analyzers.
+        connectors_requested (List[str], optional):
+            List of specific connectors to invoke.
+            Defaults to ``[]`` i.e. all connectors.
+        tlp (str, optional):
+            TLP for the analysis.
+            (options: ``CLEAR, GREEN, AMBER, RED``).
+        runtime_configuration (Dict, optional):
+            Overwrite configuration for analyzers. Defaults to ``{}``.
+        tags_labels (List[str], optional):
+            List of tag labels to assign (creates non-existing tags)
+        observable_classification (str):
+            Observable classification, Default to None.
+            By default launch analysis with an automatic classification.
+            (options: ``url, domain, hash, ip, generic``)
+
+    Raises:
+        IntelOwlClientException: on client/HTTP error
+        IntelOwlClientException: on wrong observable_classification
+
+    Returns:
+        Dict: JSON body
+    """
+    try:
+        if not tlp:
+            tlp = "CLEAR"
+        if not analyzers_requested:
+            analyzers_requested = []
+        if not connectors_requested:
+            connectors_requested = []
+        if not tags_labels:
+            tags_labels = []
+        if not runtime_configuration:
+            runtime_configuration = {}
+        if not observable_classification:
+            observable_classification = self._get_observable_classification(
+                observable_name
+            )
+        elif observable_classification not in [
+            "generic",
+            "hash",
+            "ip",
+            "domain",
+            "url",
+        ]:
+            raise IntelOwlClientException(
+                "Observable classification only handle"
+                " 'generic', 'hash', 'ip', 'domain' and 'url' "
+            )
+        data = {
+            "observable_name": observable_name,
+            "observable_classification": observable_classification,
+            "analyzers_requested": analyzers_requested,
+            "connectors_requested": connectors_requested,
+            "tlp": tlp,
+            "tags_labels": tags_labels,
+            "runtime_configuration": runtime_configuration,
+        }
+        answer = self.__send_analysis_request(data=data, files=None)
+    except Exception as e:
+        raise IntelOwlClientException(e)
+    return answer
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/pyintelowl/IntelOwlClientException/index.html b/pyintelowl/IntelOwlClientException/index.html new file mode 100644 index 0000000..06fb568 --- /dev/null +++ b/pyintelowl/IntelOwlClientException/index.html @@ -0,0 +1,723 @@ + + + + + + + + + + + + + +IntelOwlClientException - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

IntelOwlClientException

+

IntelOwlClientException Class

+
+ +
+

+ Bases: RequestException

+
+Source code in docs/Submodules/pyintelowl/pyintelowl/exceptions.py +
 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
class IntelOwlClientException(RequestException):
+    @property
+    def error_detail(self) -> typing.Union[typing.Dict, typing.AnyStr]:
+        content = None
+        try:
+            content = self.response.json()
+            detail = content.get("detail", None)
+            if detail:
+                content = detail
+        except json.JSONDecodeError:
+            content = self.response.content
+        except Exception:
+            pass
+
+        return content
+
+    def __str__(self):
+        err_msg = super().__str__()
+        detail = self.error_detail
+        return err_msg + f". Details: {detail}"
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/pyintelowl/Tests/index.html b/pyintelowl/Tests/index.html new file mode 100644 index 0000000..166ff24 --- /dev/null +++ b/pyintelowl/Tests/index.html @@ -0,0 +1,710 @@ + + + + + + + + + + + + + +Tests - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + +

Tests

+

Configuration

+

Some tests require file samples, which can be found in the encrypted folder tests/test_files.zip (password: "infected"). +Unzip the archive in tests/test_files folder before running the tests.

+

Please remember that these are dangerous malware! They come encrypted and locked for a reason! +Do NOT run them unless you are absolutely sure of what you are doing! +They are to be used only for launching specific tests that require them (__send_analysis_request)

+
    +
  • +

    With the following constants in __init__.py, you can customize your tests:

    +
  • +
  • +

    MOCKING_CONNECTIONS: Mock connections to external API to test functions without a real connection or a valid API Key.

    +
  • +
  • +

    If you prefer to use custom inputs for tests, you can change the following constants:

    +
  • +
  • +

    TEST_JOB_ID

    +
  • +
  • TEST_HASH
  • +
  • TEST_URL
  • +
  • TEST_IP
  • +
  • TEST_DOMAIN
  • +
  • TEST_GENERIC
  • +
  • TEST_FILE
  • +
  • TEST_FILE_HASH
  • +
+

Launch Tests

+
    +
  • The test requirements are specified in the test-requirements.txt file. Install them using,
  • +
+

.. code-block:: bash

+
$ pip3 install -r test-requirements.txt
+
+
    +
  • Launch the tests using tox:
  • +
+

.. code-block:: bash

+
$ tox
+
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/pyintelowl/index.html b/pyintelowl/index.html new file mode 100644 index 0000000..4333945 --- /dev/null +++ b/pyintelowl/index.html @@ -0,0 +1,847 @@ + + + + + + + + + + + + + +Quickstart - IntelOwl Documentation + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ + + + + + +

PyIntelOwl Repository

+

Welcome to PyIntelOwl's documentation!

+

Robust Python SDK and Command Line Client for interacting with IntelOwl API.

+

Installation

+
pip install pyintelowl
+
+

Usage as CLI

+
 pyintelowl
+ Usage: pyintelowl [OPTIONS] COMMAND [ARGS]...
+
+ Options:
+ -d, --debug  Set log level to DEBUG
+ --version    Show the version and exit.
+ -h, --help   Show this message and exit.
+
+ Commands:
+ analyse                Send new analysis request
+ analyzer-healthcheck   Send healthcheck request for an analyzer...
+ config                 Set or view config variables
+ connector-healthcheck  Send healthcheck request for a connector
+ get-analyzer-config    Get current state of `analyzer_config.json` from...
+ get-connector-config   Get current state of `connector_config.json` from...
+ get-playbook-config    Get current state of `playbook_config.json` from...
+ jobs                   Manage Jobs
+ tags                   Manage tags
+
+

Configuration:

+

You can use set to set the config variables and get to view them.

+
pyintelowl config set -k 4bf03f20add626e7138f4023e4cf52b8 -u "http://localhost:80"
+pyintelowl config get
+
+

Hint

+

The CLI would is well-documented which will help you navigate various commands easily. Invoke pyintelowl -h or pyintelowl <command> -h to get help.

+

Usage as SDK/library

+
 from pyintelowl import IntelOwl, IntelOwlClientException
+ obj = IntelOwl(
+    "4bf03f20add626e7138f4023e4cf52b8",
+    "http://localhost:80",
+    None,
+ )
+ """
+ obj = IntelOwl(
+    "<your_api_key>",
+    "<your_intelowl_instance_url>",
+    "optional<path_to_pem_file>"
+    "optional<proxies>"
+ )
+ """
+
+ try:
+    ans = obj.get_analyzer_configs()
+    print(ans)
+ except IntelOwlClientException as e:
+    print("Oh no! Error: ", e)
+
+

Tip

+

We very much recommend going through the :class:pyintelowl.pyintelowl.IntelOwl docs.

+

Index

+
.. toctree::
+   :maxdepth: 2
+   :caption: Usage
+
+   pyintelowl
+
+
  .. toctree::
+   :maxdepth: 2
+   :caption: Development
+
+   tests
+
+
+
+ +
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..43efee7 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-,:!=\\[\\]()\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"IntelOwl Centralised Documentation","text":"

Welcome to the IntelOwl Centralised Documentation. Here you will be able to find all documentation for all projects under IntelOwl.

"},{"location":"#introduction","title":"Introduction","text":"

Intel Owl is an Open Source Intelligence, or OSINT solution, to get Threat Intelligence data about a specific digital artifact from a single API at scale. It integrates a high number of services available online and a lot of cutting-edge malware analysis tools. It is for everyone who needs a single point to query for info about a specific file or observable. If you are a Security Analyst, do not waste any more time in performing enrichment tasks! IntelOwl saves your time and allows you to concentrate on more serious tasks.

"},{"location":"#getting-started","title":"Getting started","text":"
  • Introduction More

  • Installation More

  • Contribute More

  • Usage More

  • Advanced Usage More

  • Advanced Configuration More

Need more help?

We are doing our best to keep this documentation complete, accurate and up to date.

If you still have questions or you find something which is not sufficiently explained, join the IntelOwl channel under HoneyNet Community on Slack.

"},{"location":"Guide-docstrings/","title":"Docstrings guide","text":""},{"location":"Guide-docstrings/#implementing-docstrings-in-python-code","title":"Implementing Docstrings in Python Code","text":"

When you write or modify Python code in the codebase, it's important to add or update the docstrings accordingly. If you wish to display these docstrings in the documentation, follow these steps.

Suppose the docstrings are located in the following path: docs/Submodules/IntelOwl/api_app/analyzers_manager/classes, and you want to show the description of a class, such as BaseAnalyzerMixin.

To include this in the documentation, use the following command:

:::docs.Submodules.IntelOwl.api_app.analyzers_manager.classes.BaseAnalyzerMixin\n

Warning

Make sure your path is correct and syntax is correct. If you face any issues even path is correct then read the Submodules Guide."},{"location":"Guide-docstrings/#this-is-how-it-would-look-in-documentation","title":"This is how it would look in documentation:","text":"

Bases: Plugin

Abstract Base class for Analyzers. Never inherit from this branch, always use either one of ObservableAnalyzer or FileAnalyzer classes.

Source code in docs/Submodules/IntelOwl/api_app/analyzers_manager/classes.py
class BaseAnalyzerMixin(Plugin, metaclass=ABCMeta):\n    \"\"\"\n    Abstract Base class for Analyzers.\n    Never inherit from this branch,\n    always use either one of ObservableAnalyzer or FileAnalyzer classes.\n    \"\"\"\n\n    HashChoices = HashChoices\n    ObservableTypes = ObservableTypes\n    TypeChoices = TypeChoices\n\n    @classmethod\n    @property\n    def config_exception(cls):\n        return AnalyzerConfigurationException\n\n    @property\n    def analyzer_name(self) -> str:\n        return self._config.name\n\n    @classmethod\n    @property\n    def report_model(cls):\n        return AnalyzerReport\n\n    @classmethod\n    @property\n    def config_model(cls):\n        return AnalyzerConfig\n\n    def get_exceptions_to_catch(self):\n        \"\"\"\n        Returns additional exceptions to catch when running *start* fn\n        \"\"\"\n        return (\n            AnalyzerConfigurationException,\n            AnalyzerRunException,\n        )\n\n    def _validate_result(self, result, level=0, max_recursion=190):\n        \"\"\"\n        function to validate result, allowing to store inside postgres without errors.\n\n        If the character \\u0000 is present in the string, postgres will throw an error\n\n        If an integer is bigger than max_int,\n        Mongodb is not capable to store and will throw an error.\n\n        If we have more than 200 recursion levels, every encoding\n        will throw a maximum_nested_object exception\n        \"\"\"\n        if level == max_recursion:\n            logger.info(\n                f\"We have reached max_recursion {max_recursion} level. \"\n                f\"The following object will be pruned {result} \"\n            )\n            return None\n        if isinstance(result, dict):\n            for key, values in result.items():\n                result[key] = self._validate_result(\n                    values, level=level + 1, max_recursion=max_recursion\n                )\n        elif isinstance(result, list):\n            for i, _ in enumerate(result):\n                result[i] = self._validate_result(\n                    result[i], level=level + 1, max_recursion=max_recursion\n                )\n        elif isinstance(result, str):\n            return result.replace(\"\\u0000\", \"\")\n        elif isinstance(result, int) and result > 9223372036854775807:  # max int 8bytes\n            result = 9223372036854775807\n        return result\n\n    def after_run_success(self, content):\n        super().after_run_success(self._validate_result(content, max_recursion=15))\n
"},{"location":"Guide-docstrings/#docs.Submodules.IntelOwl.api_app.analyzers_manager.classes.BaseAnalyzerMixin.get_exceptions_to_catch","title":"get_exceptions_to_catch()","text":"

Returns additional exceptions to catch when running start fn

Source code in docs/Submodules/IntelOwl/api_app/analyzers_manager/classes.py
def get_exceptions_to_catch(self):\n    \"\"\"\n    Returns additional exceptions to catch when running *start* fn\n    \"\"\"\n    return (\n        AnalyzerConfigurationException,\n        AnalyzerRunException,\n    )\n
"},{"location":"Guide-documentation/","title":"Setting Up the New Documentation Site Locally","text":"

To set up and run the documentation site on your local machine, please follow the steps below:

"},{"location":"Guide-documentation/#1-create-a-virtual-environment","title":"1. Create a Virtual Environment","text":"

To create a virtual environment named venv in your project directory, use the following command:

python3 -m venv venv\n
"},{"location":"Guide-documentation/#2-activate-the-virtual-environment","title":"2. Activate the Virtual Environment","text":"

Activate the virtual environment to ensure that all dependencies are installed locally within your project directory.

On Linux/MacOS:

source venv/bin/activate\n

On Windows:

venv\\Scripts\\activate\n
"},{"location":"Guide-documentation/#3-install-dependencies","title":"3. Install Dependencies","text":"

To install all the necessary Python packages listed in requirements.txt, run:

pip install -r requirements.txt\n

Please run these commands to update and fetch the local Submodules.

git submodule foreach --recursive 'git fetch --all'\ngit submodule update --init --remote --recursive --depth 1\ngit submodule sync --recursive\ngit submodule update --remote --recursive\n
"},{"location":"Guide-documentation/#4-serve-the-documentation-locally","title":"4. Serve the Documentation Locally","text":"

Start a local development server to preview the documentation in your web browser. The server will automatically reload whenever you make changes to the documentation files.

mkdocs serve\n
"},{"location":"Guide-documentation/#5-make-changes-and-review","title":"5. Make Changes and Review","text":"

As you edit the documentation, you can view your changes in real-time through the local server. This step ensures everything looks as expected before deploying.

"},{"location":"Guide-documentation/#6-push-changes-to-github","title":"6. Push Changes to GitHub","text":"

Once you are satisfied with your changes, commit and push them to the GitHub repository. The documentation will be automatically deployed via GitHub Actions, making it live on the documentation site.

"},{"location":"Submodules/","title":"Submodules","text":""},{"location":"Submodules/#implementing-docstrings-in-intelowl-documentation","title":"Implementing Docstrings in IntelOwl Documentation","text":"

In the IntelOwl documentation site, we use Git submodules to manage multiple repositories as child repositories. This allows us to fetch updated code (including docstrings and API specs) automatically, reducing redundant work for developers.

"},{"location":"Submodules/#current-submodules","title":"Current Submodules","text":"

There are four submodules under the IntelOwlProject:

  1. IntelOwl
  2. GreedyBear
  3. pyintelowl
  4. GoIntelOwl

These submodules are updated whenever we push new changes to our documentation site, here's the Github Action file.

"},{"location":"Submodules/#making-changes-to-documentation","title":"Making Changes to Documentation","text":"

When you make changes to the IntelOwl codebase, it typically does not update automatically in the github repository of documentation site.

While development if you want to update the submodules to latest changes you can do the following:

git submodule foreach --recursive 'git fetch --all'\ngit submodule update --init --remote --recursive --depth 1\ngit submodule sync --recursive\ngit submodule update --remote --recursive\n

However, if you need to test changes immediately, you can do the following:

"},{"location":"Submodules/#add-custom-submodules-for-testing","title":"Add Custom Submodules for Testing:","text":"

Point the submodule in .gitmodules to your fork of the repository to check the updates instantly.

"},{"location":"Submodules/#update-submodules","title":"Update Submodules:","text":"

After modifying .gitmodules, run the following command to fetch the latest changes:

git submodule update --remote --merge\n

This ensures that your documentation reflects the most recent code changes.

"},{"location":"GoIntelOwl/","title":"index","text":"

Go-IntelOwl Repository

"},{"location":"GoIntelOwl/#go-intelowl","title":"go-intelowl","text":"

go-intelowl is a client library/SDK that allows developers to easily automate and integrate IntelOwl with their own set of tools!

"},{"location":"GoIntelOwl/#table-of-contents","title":"Table of Contents","text":"
  • go-intelowl
  • Getting Started
  • Pre requisites
  • Installation
  • Usage
  • Examples
  • Contribute
  • License
  • Links
  • FAQ
  • Generate API key
    • v4.0 and above
    • v4.0 below
"},{"location":"GoIntelOwl/#getting-started","title":"Getting Started","text":""},{"location":"GoIntelOwl/#pre-requisites","title":"Pre requisites","text":"
  • Go 1.17+
"},{"location":"GoIntelOwl/#installation","title":"Installation","text":"

Use go get to retrieve the SDK to add it to your GOPATH workspace, or project's Go module dependencies.

$ go get github.com/intelowlproject/go-intelowl\n
"},{"location":"GoIntelOwl/#usage","title":"Usage","text":"

This library was built with ease of use in mind! Here are some quick examples to get you started. If you need more example you can go to the examples directory

To start using the go-intelowl library you first need to import it:

import \"github.com/intelowlproject/go-intelowl/gointelowl\"\n

Construct a new IntelOwlClient, then use the various services to easily access different parts of Intelowl's REST API. Here's an example of getting all jobs:

clientOptions := gointelowl.IntelOwlClientOptions{\n    Url:         \"your-cool-URL-goes-here\",\n    Token:       \"your-super-secret-token-goes-here\",\n    // This is optional\n    Certificate: \"your-optional-certificate-goes-here\",\n}\n\nintelowl := gointelowl.NewIntelOwlClient(\n    &clientOptions,\n    nil\n)\n\nctx := context.Background()\n\n// returns *[]Jobs or an IntelOwlError!\njobs, err := intelowl.JobService.List(ctx)\n

For easy configuration and set up we opted for options structs. Where we can customize the client API or service endpoint to our liking! For more information go here. Here's a quick example!

// ...Making the client and context!\n\ntagOptions = gointelowl.TagParams{\n  Label: \"NEW TAG\",\n  Color: \"#ffb703\",\n}\n\ncreatedTag, err := intelowl.TagService.Create(ctx, tagOptions)\nif err != nil {\n    fmt.Println(err)\n} else {\n    fmt.Println(createdTag)\n}\n
"},{"location":"GoIntelOwl/#examples","title":"Examples","text":"

The examples directory contains a couple for clear examples, of which one is partially listed here as well:

package main\n\nimport (\n    \"fmt\"\n\n    \"github.com/intelowlproject/go-intelowl/gointelowl\"\n)\n\nfunc main(){\n    intelowlOptions := gointelowl.IntelOwlClientOptions{\n        Url:         \"your-cool-url-goes-here\",\n        Token:       \"your-super-secret-token-goes-here\",\n        Certificate: \"your-optional-certificate-goes-here\",\n    }\n\n    client := gointelowl.NewIntelOwlClient(\n        &intelowlOptions,\n        nil,\n    )\n\n    ctx := context.Background()\n\n    // Get User details!\n    user, err := client.UserService.Access(ctx)\n    if err != nil {\n        fmt.Println(\"err\")\n        fmt.Println(err)\n    } else {\n        fmt.Println(\"USER Details\")\n        fmt.Println(*user)\n    }\n}\n

For complete usage of go-intelowl, see the full package docs.

"},{"location":"GoIntelOwl/#contribute","title":"Contribute","text":"

If you want to follow the updates, discuss, contribute, or just chat then please join our slack channel we'd love to hear your feedback!

"},{"location":"GoIntelOwl/#license","title":"License","text":"

Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE.

"},{"location":"GoIntelOwl/#links","title":"Links","text":"
  • Intelowl
  • Documentation
  • API documentation
  • Examples
"},{"location":"GoIntelOwl/#faq","title":"FAQ","text":""},{"location":"GoIntelOwl/#generate-api-key","title":"Generate API key","text":"

You need a valid API key to interact with the IntelOwl server.

"},{"location":"GoIntelOwl/#v40-and-above","title":"v4.0 and above","text":"

You can get an API by doing the following:

  1. Log / Signin into intelowl
  2. At the upper right click on your profile from the drop down select API Access/ Sessions
  3. Then generate an API key or see it!
"},{"location":"GoIntelOwl/#v40-below","title":"v4.0 below","text":"

Keys should be created from the admin interface of IntelOwl: you have to go in the Durin section (click on Auth tokens) and generate a key there.

"},{"location":"GreedyBear/Api-docs/","title":"API docs","text":""},{"location":"GreedyBear/Contribute/","title":"Contribute","text":""},{"location":"GreedyBear/Contribute/#general-guidance","title":"General Guidance","text":"

Please refer to IntelOwl Documentation for everything missing here.

"},{"location":"GreedyBear/Contribute/#rules","title":"Rules","text":"

GreedyBear welcomes contributors from anywhere and from any kind of education or skill level. We strive to create a community of developers that is welcoming, friendly and right.

For this reason it is important to follow some easy rules based on a simple but important concept: Respect.

  • Before starting to work on an issue, you need to get the approval of one of the maintainers. Therefore please ask to be assigned to an issue. If you do not that but you still raise a PR for that issue, your PR can be rejected. This is a form of respect for both the maintainers and the other contributors who could have already started to work on the same problem.

  • When you ask to be assigned to an issue, it means that you are ready to work on it. When you get assigned, take the lock and then you disappear, you are not respecting the maintainers and the other contributors who could be able to work on that. So, after having been assigned, you have a week of time to deliver your first draft PR. After that time has passed without any notice, you will be unassigned.

  • Before asking questions regarding how the project works, please read through all the documentation and install the project on your own local machine to try it and understand how it basically works. This is a form of respect to the maintainers.

  • Once you started working on an issue and you have some work to share and discuss with us, please raise a draft PR early with incomplete changes. This way you can continue working on the same and we can track your progress and actively review and help. This is a form of respect to you and to the maintainers.

  • When creating a PR, please read through the sections that you will find in the PR template and compile it appropriately. If you do not, your PR can be rejected. This is a form of respect to the maintainers.

"},{"location":"GreedyBear/Contribute/#code-style","title":"Code Style","text":"

Keeping to a consistent code style throughout the project makes it easier to contribute and collaborate. We make use of psf/black and isort for code formatting and flake8 for style guides.

"},{"location":"GreedyBear/Contribute/#how-to-start-setup-project-and-development-instance","title":"How to start (Setup project and development instance)","text":"

To start with the development setup, make sure you go through all the steps in Installation Guide and properly installed it.

Please create a new branch based on the develop branch that contains the most recent changes. This is mandatory.

git checkout -b myfeature develop

Then we strongly suggest to configure pre-commit to force linters on every commits you perform:

# create virtualenv to host pre-commit installation\npython3 -m venv venv\nsource venv/bin/activate\n# from the project base directory\npip install pre-commit\npre-commit install -c .github/.pre-commit-config.yaml\n

Remember that whenever you make changes, you need to rebuild the docker image to see the reflected changes.

"},{"location":"GreedyBear/Contribute/#note-about-documentation","title":"NOTE about documentation:","text":"

If you made any changes to an existing model/serializer/view, please run the following command to generate a new version of the API schema and docs:

docker exec -it greedybear_uwsgi python manage.py spectacular --file docs/source/schema.yml && make html\n
"},{"location":"GreedyBear/Contribute/#frontend","title":"Frontend","text":"

To start the frontend in \"develop\" mode, you can execute the startup npm script within the folder frontend:

cd frontend/\n# Install\nnpm i\n# Start\nDANGEROUSLY_DISABLE_HOST_CHECK=true npm start\n# See https://create-react-app.dev/docs/proxying-api-requests-in-development/#invalid-host-header-errors-after-configuring-proxy for why we use that flag in development mode\n

Most of the time you would need to test the changes you made together with the backend. In that case, you would need to run the backend locally too:

docker-compose up\n
"},{"location":"GreedyBear/Contribute/#certego-ui","title":"Certego-UI","text":"

The GreedyBear Frontend is tightly linked to the certego-ui library. Most of the React components are imported from there. Because of this, it may happen that, during development, you would need to work on that library too. To install the certego-ui library, please take a look to npm link and remember to start certego-ui without installing peer dependencies (to avoid conflicts with GreedyBear dependencies):

git clone https://github.com/certego/certego-ui.git\n# change directory to the folder where you have the cloned the library\ncd certego-ui/\n# install, without peer deps (to use packages of GreedyBear)\nnpm i --legacy-peer-deps\n# create link to the project (this will globally install this package)\nsudo npm link\n# compile the library\nnpm start\n

Then, open another command line tab, create a link in the frontend to the certego-ui and re-install and re-start the frontend application (see previous section):

cd frontend/\nnpm link @certego/certego-ui\n

This trick will allow you to see reflected every changes you make in the certego-ui directly in the running frontend application.

"},{"location":"GreedyBear/Contribute/#example-application","title":"Example application","text":"

The certego-ui application comes with an example project that showcases the components that you can re-use and import to other projects, like GreedyBear:

# To have the Example application working correctly, be sure to have installed `certego-ui` *without* the `--legacy-peer-deps` option and having it started in another command line\ncd certego-ui/\nnpm i\nnpm start\n# go to another tab\ncd certego-ui/example/\nnpm i\nnpm start\n
"},{"location":"GreedyBear/Contribute/#create-a-pull-request","title":"Create a pull request","text":""},{"location":"GreedyBear/Contribute/#remember","title":"Remember!!!","text":"

Please create pull requests only for the branch develop. That code will be pushed to master only on a new release.

Also remember to pull the most recent changes available in the develop branch before submitting your PR. If your PR has merge conflicts caused by this behavior, it won't be accepted.

"},{"location":"GreedyBear/Contribute/#tests","title":"Tests","text":""},{"location":"GreedyBear/Contribute/#backend","title":"Backend","text":""},{"location":"GreedyBear/Contribute/#install-testing-requirements","title":"Install testing requirements","text":"

You have to install pre-commit to have your code adjusted and fixed with the available linters:

pip install pre-commit\npre-commit install -c .github/.pre-commit-config.yaml\n

Once done that, you won't have to think about linters anymore.

"},{"location":"GreedyBear/Contribute/#run-all-tests","title":"Run all tests","text":"
docker exec greedybear_uwsgi python3 manage.py test\n
"},{"location":"GreedyBear/Contribute/#frontend_1","title":"Frontend","text":"

All the frontend tests must be run from the folder frontend. The tests can contain log messages, you can suppress then with the environment variable SUPPRESS_JEST_LOG=True.

"},{"location":"GreedyBear/Contribute/#run-all-tests_1","title":"Run all tests","text":"
npm test\n
"},{"location":"GreedyBear/Contribute/#run-a-specific-component-tests","title":"Run a specific component tests","text":"
npm test -- -t <componentPath>\n// example\nnpm test tests/components/auth/Login.test.jsx\n
"},{"location":"GreedyBear/Contribute/#run-a-specific-test","title":"Run a specific test","text":"
npm test -- -t '<describeString> <testString>'\n// example\nnpm test -- -t \"Login component User login\"\n

if you get any errors, fix them. Once you make sure that everything is working fine, please squash all of our commits into a single one and finally create a pull request.

"},{"location":"GreedyBear/Installation/","title":"Installation","text":"

Start by cloning the project

# clone the Greedybear project repository\ngit clone https://github.com/honeynet/GreedyBear\ncd GreedyBear/\n\n# construct environment files from templates\ncp .env_template .env\ncd docker/\ncp env_file_template env_file\ncp env_file_postgres_template env_file_postgres\ncd ..\ncp frontend/public/env_template.js frontend/public/env.js\n

Now you can start by building the image using docker-compose and run the project.

# build the image locally\ndocker-compose build\n\n# start the app\ndocker-compose up\n\n# now the app is running on http://localhost:80\n\n# shut down the application\ndocker-compose down\n

Note: To create a superuser run the following:

docker exec -ti greedybear_uwsgi python3 manage.py createsuperuser\n

The app administrator can enable/disable the extraction of source IPs for specific honeypots from the Django Admin. This is used for honeypots that are not specifically implemented to extract additional information (so not Log4Pot and Cowrie).

Note that GreedyBear needs a running instance of ElasticSearch of a TPoT to function. If you don't have one, you can make the following changes to make GreeyBear spin up it's own ElasticSearch and Kibana instances. (...Care! This option would require enough RAM to run the additional containers. Suggested is >=16GB):

  1. In docker/env_file, set the variable ELASTIC_ENDPOINT to http://elasticsearch:9200.
  2. Add :docker/elasticsearch.yml to the last defined COMPOSE_FILE variable or uncomment the # local development with elasticsearch container block in .env file.
"},{"location":"GreedyBear/Installation/#environment-configuration","title":"Environment configuration","text":"

In the env_file, configure different variables as explained below.

Required variable to set:

  • DEFAULT_FROM_EMAIL: email address used for automated correspondence from the site manager (example: noreply@mydomain.com)
  • DEFAULT_EMAIL: email address used for correspondence with users (example: info@mydomain.com)
  • EMAIL_HOST: the host to use for sending email with SMTP
  • EMAIL_HOST_USER: username to use for the SMTP server defined in EMAIL_HOST
  • EMAIL_HOST_PASSWORD: password to use for the SMTP server defined in EMAIL_HOST. This setting is used in conjunction with EMAIL_HOST_USER when authenticating to the SMTP server.
  • EMAIL_PORT: port to use for the SMTP server defined in EMAIL_HOST.
  • EMAIL_USE_TLS: whether to use an explicit TLS (secure) connection when talking to the SMTP server, generally used on port 587.
  • EMAIL_USE_SSL: whether to use an implicit TLS (secure) connection when talking to the SMTP server, generally used on port 465.

Optional configuration:

  • SLACK_TOKEN: Slack token of your Slack application that will be used to send/receive notifications
  • DEFAULT_SLACK_CHANNEL: ID of the Slack channel you want to post the message to
"},{"location":"GreedyBear/Installation/#recaptcha-configuration","title":"Recaptcha configuration","text":"

The Registration Page and the Login Page contain a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown. If your intention is to publish GreedyBear as a Service you should configure different variables as explained below.

In the frontend/public/env.js set the variable:

  • RECAPTCHA_SITEKEY: Recaptcha Key for your site

In the docker/env_file set the variables:

  • RECAPTCHA_SECRET_KEY_GB_LOCAL: your recaptcha secret key internal deployment
  • RECAPTCHA_SECRET_KEY_GB_PUBLIC: your recaptcha secret key for public deployment

In that case, you would need to re-build the application to have the changes properly reflected.

"},{"location":"GreedyBear/Installation/#update-and-re-build","title":"Update and Re-build","text":""},{"location":"GreedyBear/Installation/#rebuilding-the-project-creating-custom-docker-build","title":"Rebuilding the project / Creating custom docker build","text":"

If you make some code changes and you like to rebuild the project, follow these steps:

  1. Be sure that your .env file has a COMPOSE_FILE variable which mounts the docker/local.override.yml compose file.
  2. docker-compose build to build the new docker image.
  3. Start the containers with docker-compose up.
"},{"location":"GreedyBear/Installation/#update-to-the-most-recent-version","title":"Update to the most recent version","text":"

To update the project with the most recent available code you have to follow these steps:

$ cd <your_greedy_bear_directory> # go into the project directory\n$ git pull # pull new repository changes\n$ docker pull intelowlproject/greedybear:prod # pull new docker images\n$ docker-compose down # stop and destroy the currently running GreedyBear containers\n$ docker-compose up # restart the GreedyBear application\n
"},{"location":"GreedyBear/Installation/#installer-for-tpot-instance","title":"Installer for TPot Instance","text":"

The file 'installer_on_tpot.sh' allows the automatic installation of Greedybear on an existing TPot instance. You can choose the type of Greedybear you want to install (http, https or local). The installer will either clone Greedybear to '/opt/GreedyBear' or if Greedybear exists on your system you need to input the absolute path to the existing Greedybear folder. It will prompt you for the necessary information/secrets needed.

Example: sudo ./installer.sh --type=http --folder=/opt/GreedyBear

Warning

This installer is not officialy supported neither by Greedybear nor by TPOT maintainers. It must be considered as a POC to have GB and TPOT installed in the same place. Greedybear is supported to be executed only in a separate instance and to connect externally with the TPOTs."},{"location":"GreedyBear/Introduction/","title":"Introduction","text":"

GreedyBear Repository

"},{"location":"GreedyBear/Introduction/#introduction","title":"Introduction","text":"

The project goal is to extract data of the attacks detected by a TPOT or a cluster of them and to generate some feeds that can be used to prevent and detect attacks.

Official announcement here.

"},{"location":"GreedyBear/Introduction/#public-feeds","title":"Public feeds","text":"

There are public feeds provided by The Honeynet Project in this site: greedybear.honeynet.org. Example

To check all the available feeds, Please refer to our usage guide

Please do not perform too many requests to extract feeds or you will be banned.

If you want to be updated regularly, please download the feeds only once every 10 minutes (this is the time between each internal update).

"},{"location":"GreedyBear/Usage/","title":"Usage","text":""},{"location":"GreedyBear/Usage/#user-management","title":"User management","text":""},{"location":"GreedyBear/Usage/#registration","title":"Registration","text":"

Since Greedybear v1.1.0 we added a Registration Page that can be used to manage Registration requests when providing GreedyBear as a Service.

After an user registration, an email is sent to the user to verify their email address. If necessary, there are buttons on the login page to resend the verification email and to reset the password.

Once the user has verified their email, they would be manually vetted before being allowed to use the GreedyBear platform. The registration requests would be handled in the Django Admin page by admins. If you have GreedyBear deployed on an AWS instance you can use the SES service.

In a development environment the emails that would be sent are written to the standard output.

"},{"location":"GreedyBear/Usage/#recaptcha-configuration","title":"Recaptcha configuration","text":"

The Registration Page contains a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown. If your intention is to publish GreedyBear as a Service you should configure the Recaptcha.

"},{"location":"GreedyBear/Usage/#amazon-ses","title":"Amazon SES","text":"

If you like, you could use Amazon SES for sending automated emails.

First, you need to configure the environment variable AWS_SES to True to enable it. Then you have to add some credentials for AWS: if you have GreedyBear deployed on the AWS infrastructure, you can use IAM credentials: to allow that just set AWS_IAM_ACCESS to True. If that is not the case, you have to set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

Additionally, if you are not using the default AWS region of us-east-1, you need to specify your AWS_REGION. You can customize the AWS Region location of you services by changing the environment variable AWS_REGION. Default is eu-central-1.

"},{"location":"GreedyBear/Usage/#feeds","title":"Feeds","text":"

GreedyBear is created with the aim to collect the information from the TPOTs and generate some actionable feeds, so that they can be easily accessible and act as valuable information to prevent and detect attacks.

The feeds are reachable through the following URL:

https://<greedybear_site>/api/feeds/<feed_type>/<attack_type>/<age>.<format>\n

The available feed_type are:

  • log4j: attacks detected from the Log4pot.
  • cowrie: attacks detected from the Cowrie Honeypot.
  • all: get all types at once
  • The following honeypot feeds exist (for extraction of (only) the source IPs):
  • heralding
  • ciscoasa
  • honeytrap
  • dionaea
  • conpot
  • adbhoney
  • tanner
  • citrixhoneypot
  • mailoney
  • ipphoney
  • ddospot
  • elasticpot
  • dicompot
  • redishoneypot
  • sentrypeer
  • glutton

The available attack_type are:

  • scanner: IP addresses captured by the honeypots while performing attacks
  • payload_request: IP addresses and domains extracted from payloads that would have been executed after a speficic attack would have been successful
  • all: get all types at once

The available age are:

  • recent: most recent IOCs seen in the last 3 days
  • persistent: these IOCs are the ones that were seen regularly by the honeypots. This feeds will start empty once no prior data was collected and will become bigger over time.

The available formats are:

  • txt: plain text (just one line for each IOC)
  • csv: CSV-like file (just one line for each IOC)
  • json: JSON file with additional information regarding the IOCs

Check the API specification or the to get all the details about how to use the available APIs.

"},{"location":"GreedyBear/Usage/#enrichment","title":"Enrichment","text":"

GreedyBear provides an easy-to-query API to get the information available in GB regarding the queried observable (domain or IP address).

https://<greedybear_site>/api/enrichment?query=<observable>\n

This \"Enrichment\" API is protected through authentication. Please reach out Matteo Lodi or another member of The Honeynet Project if you are interested in gain access to this API.

If you would like to leverage this API without the need of writing even a line of code and together with a lot of other awesome tools, consider using IntelOwl.

"},{"location":"IntelOwl/","title":"Index","text":"

This is a Documentation for IntelOwl.

"},{"location":"IntelOwl/advanced_configuration/","title":"Advanced Configuration","text":"

This page includes details about some advanced features that Intel Owl provides which can be optionally configured by the administrator.

"},{"location":"IntelOwl/advanced_configuration/#elasticsearch","title":"ElasticSearch","text":"

Right now only ElasticSearch v8 is supported.

"},{"location":"IntelOwl/advanced_configuration/#dsl","title":"DSL","text":"

IntelOwl makes use of django-elasticsearch-dsl to index Job results into elasticsearch. The save and delete operations are auto-synced so you always have the latest data in ES.

In the env_file_app_template, you'd see various elasticsearch related environment variables. The user should spin their own Elastic Search instance and configure these variables.

"},{"location":"IntelOwl/advanced_configuration/#kibana","title":"Kibana","text":"

Intel Owl provides a Kibana's \"Saved Object\" configuration (with example dashboard and visualizations). It can be downloaded from here and can be imported into Kibana by going to the \"Saved Objects\" panel (http://localhost:5601/app/management/kibana/objects).

"},{"location":"IntelOwl/advanced_configuration/#example-configuration","title":"Example Configuration","text":"
  1. Setup Elastic Search and Kibana and say it is running in a docker service with name elasticsearch on port 9200 which is exposed to the shared docker network. (Alternatively, you can spin up a local Elastic Search instance, by appending --elastic to the ./start command. Note that the local Elastic Search instance consumes large amount of memory, and hence having >=16GB is recommended.))
  2. In the env_file_app, we set ELASTICSEARCH_DSL_ENABLED to True and ELASTICSEARCH_DSL_HOST to elasticsearch:9200.
  3. Now start the docker containers and execute
docker exec -ti intelowl_uwsgi python manage.py search_index --rebuild\n

This will build and populate all existing job objects into the jobs index.

"},{"location":"IntelOwl/advanced_configuration/#business-intelligence","title":"Business Intelligence","text":"

IntelOwl makes use of elasticsearch-py to store data that can be used for Business Intelligence purpose. Since plugin reports are deleted periodically, this feature allows to save indefinitely small amount of data to keep track of how analyzers perform and user usage. At the moment, the following information are sent to elastic:

  • application name
  • timestamp
  • username
  • configuration used
  • process_time
  • status
  • end_time
  • parameters

Documents are saved in the ELEASTICSEARCH_BI_INDEX-%YEAR-%MONTH, allowing to manage the retention accordingly. To activate this feature, it is necessary to set ELASTICSEARCH_BI_ENABLED to True in the env_file_app and ELASTICSEARCH_BI_HOST to elasticsearch:9200 or your elasticsearch server.

An index template is created after the first bulk submission of reports. If you want to use kibana to visualize your data/make dashboard, you must create an index pattern: Go to Kibana -> Discover -> Stack Management -> Index Patterns -> search for your index and use as time field timestamp

"},{"location":"IntelOwl/advanced_configuration/#authentication-options","title":"Authentication options","text":"

IntelOwl provides support for some of the most common authentication methods:

  • Google Oauth2
  • LDAP
  • RADIUS
"},{"location":"IntelOwl/advanced_configuration/#google-oauth2","title":"Google OAuth2","text":"

The first step is to create a Google Cloud Platform project, and then create OAuth credentials for it.

It is important to add the correct callback in the \"Authorized redirect URIs\" section to allow the application to redirect properly after the successful login. Add this:

http://<localhost|yourowndomain>/api/auth/google-callback\n

After that, specify the client ID and secret as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables and restart IntelOwl to see the applied changes.

Note

While configuring Google Auth2 you can choose either to enable access to the all users with a Google Account (\"External\" mode) or to enable access to only the users of your organization (\"Internal\" mode). Reference"},{"location":"IntelOwl/advanced_configuration/#ldap","title":"LDAP","text":"

IntelOwl leverages Django-auth-ldap to perform authentication via LDAP.

How to configure and enable LDAP on Intel Owl?

  1. Change the values with your LDAP configuration inside configuration/ldap_config.py. This file is mounted as a docker volume, so you won't need to rebuild the image.

Note

For more details on how to configure this file, check the official documentation of the django-auth-ldap library.
  1. Once you have done that, you have to set the environment variable LDAP_ENABLED as True in the environment configuration file env_file_app. Finally, you can restart the application with docker-compose up
"},{"location":"IntelOwl/advanced_configuration/#radius-authentication","title":"RADIUS Authentication","text":"

IntelOwl leverages Django-radius to perform authentication via RADIUS server.

How to configure and enable RADIUS authentication on Intel Owl?

  1. Change the values with your RADIUS auth configuration inside configuration/radius_config.py. This file is mounted as a docker volume, so you won't need to rebuild the image.

Note

For more details on how to configure this file, check the official documentation of the django-radius library.
  1. Once you have done that, you have to set the environment variable RADIUS_AUTH_ENABLED as True in the environment configuration file env_file_app. Finally, you can restart the application with docker-compose up
"},{"location":"IntelOwl/advanced_configuration/#opencti","title":"OpenCTI","text":"

Like many other integrations that we have, we have an Analyzer and a Connector for the OpenCTI platform.

This allows the users to leverage these 2 popular open source projects and frameworks together.

So why we have a section here? This is because there are various compatibility problems with the official PyCTI library.

We found out (see issues in IntelOwl and PyCTI) that, most of the times, it is required that the OpenCTI version of the server you are using and the pycti version installed in IntelOwl must match perfectly.

Because of that, we decided to provide to the users the chance to customize the version of PyCTI installed in IntelOwl based on the OpenCTI version that they are using.

To do that, you would need to leverage the option --pycti-version provided by the ./start helper:

  • check the default version that would be installed by checking the description of the option --pycti-version with ./start -h
  • if the default version is different from your OpenCTI server version, you need to rebuild the IntelOwl Image with ./start test build --pycti-version <your_version>
  • then restart the project ./start test up -- --build
  • enjoy
"},{"location":"IntelOwl/advanced_configuration/#cloud-support","title":"Cloud Support","text":""},{"location":"IntelOwl/advanced_configuration/#aws-support","title":"AWS support","text":"

We have support for several AWS services.

You can customize the AWS Region location of you services by changing the environment variable AWS_REGION. Default is eu-central-1

You have to add some credentials for AWS: if you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials: to allow that just set AWS_IAM_ACCESS to True. If that is not the case, you have to set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

"},{"location":"IntelOwl/advanced_configuration/#s3","title":"S3","text":"

If you prefer to use S3 to store the analyzed samples, instead of the local storage, you can do it.

First, you need to configure the environment variable LOCAL_STORAGE to False to enable it and set AWS_STORAGE_BUCKET_NAME to the AWS bucket you want to use.

Then you need to configure permission access to the chosen S3 bucket.

"},{"location":"IntelOwl/advanced_configuration/#message-broker","title":"Message Broker","text":"

IntelOwl at the moment supports 3 different message brokers:

  • Redis (default)
  • RabbitMQ
  • Aws SQS

The default broker, if nothing is specified, is Redis.

To use RabbitMQ, you must use the option --rabbitmq when launching IntelOwl with the ./start script.

To use Aws SQS, you must use the option --sqs when launching IntelOwl with the .start script. In that case, you should create new SQS queues in AWS called intelowl-<environment>-<queue_name> and give your instances on AWS the proper permissions to access it. Moreover, you must populate the AWS_USER_NUMBER. This is required to connect in the right way to the selected SQS queues. Only FIFO queues are supported.

If you want to use a remote message broker (like an ElasticCache or AmazonMQ instance), you must populate the BROKER_URL environment variable.

It is possible to use task priority inside IntelOwl: each User has default priority of 10, and robots users (like the Ingestors) have a priority of 7. You can customize these priorities inside Django Admin, in the Authentication.User Profiles section.

"},{"location":"IntelOwl/advanced_configuration/#websockets","title":"Websockets","text":"

Redis is used for two different functions:

  • message broker
  • websockets

For this reason, a Redis instance is mandatory. You can personalize IntelOwl in two different way:

  • with a local Redis instance.

This is the default behaviour.

  • With a remote Redis instance.

You must use the option --use-external-redis when launching IntelOwl with the .start script. Moreover, you need to populate the WEBSOCKETS_URL environment variable. If you are using Redis as a message broker too, remember to populate the BROKER_URL environment variable

"},{"location":"IntelOwl/advanced_configuration/#rds","title":"RDS","text":"

If you like, you could use AWS RDS instead of PostgreSQL for your database. In that case, you should change the database required options accordingly: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD and setup your machine to access the service.

If you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials to access the Postgres DB. To allow that just set AWS_RDS_IAM_ROLE to True. In this case DB_PASSWORD is not required anymore.

Moreover, to avoid to run PostgreSQL locally, you would need to use the option --use-external-database when launching IntelOwl with the ./start script.

"},{"location":"IntelOwl/advanced_configuration/#ses","title":"SES","text":"

If you like, you could use Amazon SES for sending automated emails (password resets / registration requests, etc).

You need to configure the environment variable AWS_SES to True to enable it.

"},{"location":"IntelOwl/advanced_configuration/#secrets","title":"Secrets","text":"

You can use the \"Secrets Manager\" to store your credentials. In this way your secrets would be better protected.

Instead of adding the variables to the environment file, you should just add them with the same name on the AWS Secrets Manager and Intel Owl will fetch them transparently.

Obviously, you should have created and managed the permissions in AWS in advance and accordingly to your infrastructure requirements.

Also, you need to set the environment variable AWS_SECRETS to True to enable this mode.

"},{"location":"IntelOwl/advanced_configuration/#nfs","title":"NFS","text":"

You can use a Network File System for the shared_files that are downloaded runtime by IntelOwl (for example Yara rules).

To use this feature, you would need to add the address of the remote file system inside the .env file, and you would need to use the option --nfs when launching IntelOwl with the ./start script.

"},{"location":"IntelOwl/advanced_configuration/#google-kubernetes-engine","title":"Google Kubernetes Engine","text":"

Right now there is no official support for Kubernetes deployments.

But we have an active community. Please refer to the following blog post for an example on how to deploy IntelOwl on Google Kubernetes Engine:

Deploying Intel-Owl on GKE by Mayank Malik.

"},{"location":"IntelOwl/advanced_configuration/#queues","title":"Queues","text":""},{"location":"IntelOwl/advanced_configuration/#multi-queue","title":"Multi Queue","text":"

IntelOwl provides an additional multi-queue.override.yml compose file allowing IntelOwl users to better scale with the performance of their own architecture.

If you want to leverage it, you should add the option --multi-queue when starting the project. Example:

./start prod up --multi-queue\n

This functionality is not enabled by default because this deployment would start 2 more containers so the resource consumption is higher. We suggest to use this option only when leveraging IntelOwl massively.

"},{"location":"IntelOwl/advanced_configuration/#queue-customization","title":"Queue Customization","text":"

It is possible to define new celery workers: each requires the addition of a new container in the docker-compose file, as shown in the multi-queue.override.yml.

Moreover IntelOwl requires that the name of the workers are provided in the docker-compose file. This is done through the environment variable CELERY_QUEUES inside the uwsgi container. Each queue must be separated using the character ,, as shown in the example.

One can customize what analyzer should use what queue by specifying so in the analyzer entry in the analyzer_config.json configuration file. If no queue(s) are provided, the default queue will be selected.

"},{"location":"IntelOwl/advanced_configuration/#queue-monitoring","title":"Queue monitoring","text":"

IntelOwl provides an additional flower.override.yml compose file allowing IntelOwl users to use Flower features to monitor and manage queues and tasks

If you want to leverage it, you should add the option --flower when starting the project. Example:

./start prod up --flower\n

The flower interface is available at port 5555: to set the credentials for its access, update the environment variables

FLOWER_USER\nFLOWER_PWD\n

or change the .htpasswd file that is created in the docker directory in the intelowl_flower container.

"},{"location":"IntelOwl/advanced_configuration/#manual-usage","title":"Manual Usage","text":"

The ./start script essentially acts as a wrapper over Docker Compose, performing additional checks. IntelOwl can still be started by using the standard docker compose command, but all the dependencies have to be manually installed by the user.

"},{"location":"IntelOwl/advanced_configuration/#options","title":"Options","text":"

The --project-directory and -p options are required to run the project. Default values set by ./start script are \"docker\" and \"intel_owl\", respectively.

The startup is based on chaining various Docker Compose YAML files using -f option. All Docker Compose files are stored in docker/ directory of the project. The default compose file, named default.yml, requires configuration for an external database and message broker. In their absence, the postgres.override.yml and rabbitmq.override.yml files should be chained to the default one.

The command composed, considering what is said above (using sudo), is

sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -p intel_owl up\n

The other most common compose file that can be used is for the testing environment. The equivalent of running ./start test up is adding the test.override.yml file, resulting in:

sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -f docker/test.override.yml -p intel_owl up\n

All other options available in the ./start script (./start -h to view them) essentially chain other compose file to docker compose command with corresponding filenames.

"},{"location":"IntelOwl/advanced_configuration/#optional-analyzer","title":"Optional Analyzer","text":"

IntelOwl includes integrations with some analyzer that are not enabled by default. These analyzers, stored under the integrations/ directory, are packed within Docker Compose files. The compose.yml file has to be chained to include the analyzer. The additional compose-test.yml file has to be chained for testing environment.

"},{"location":"IntelOwl/advanced_usage/","title":"Advanced Usage","text":"

This page includes details about some advanced features that Intel Owl provides which can be optionally enabled. Namely,

"},{"location":"IntelOwl/advanced_usage/#organizations-and-user-management","title":"Organizations and User management","text":"

Starting from IntelOwl v4, a new \"Organization\" section is available on the GUI. This section substitute the previous permission management via Django Admin and aims to provide an easier way to manage users and visibility.

"},{"location":"IntelOwl/advanced_usage/#multi-tenancy","title":"Multi Tenancy","text":"

Thanks to the \"Organization\" feature, IntelOwl can be used by multiple SOCs, companies, etc...very easily. Right now it works very simply: only users in the same organization can see analysis of one another. An user can belong to an organization only.

"},{"location":"IntelOwl/advanced_usage/#manage-organizations","title":"Manage organizations","text":"

You can create a new organization by going to the \"Organization\" section, available under the Dropdown menu you cand find under the username.

Once you create an organization, you are the unique \"Owner\" of that organization. So you are the only one who can delete the organization and promote/demote/kick users. Another role, which is called \"Admin\", can be set to a user (via the Django Admin interface only for now). Owners and admins share the following powers: they can manage invitations and the organization's plugin configuration.

"},{"location":"IntelOwl/advanced_usage/#accept-invites","title":"Accept Invites","text":"

Once an invite has sent, the invited user has to login, go to the \"Organization\" section and accept the invite there. Afterwards the Administrator will be able to see the user in his \"Organization\" section.

"},{"location":"IntelOwl/advanced_usage/#plugins-params-and-secrets","title":"Plugins Params and Secrets","text":"

From IntelOwl v4.1.0, Plugin Parameters and Secrets can be defined at the organization level, in the dedicated section. This allows to share configurations between users of the same org while allowing complete multi-tenancy of the application. Only Owners and Admins of the organization can set, change and delete them.

"},{"location":"IntelOwl/advanced_usage/#disable-plugins-at-org-level","title":"Disable Plugins at Org level","text":"

The org admin can disable a specific plugin for all the users in a specific org. To do that, Org Admins needs to go in the \"Plugins\" section and click the button \"Enabled for organization\" of the plugin that they want to disable.

"},{"location":"IntelOwl/advanced_usage/#registration","title":"Registration","text":"

Since IntelOwl v4.2.0 we added a Registration Page that can be used to manage Registration requests when providing IntelOwl as a Service.

After a user registration has been made, an email is sent to the user to verify their email address. If necessary, there are buttons on the login page to resend the verification email and to reset the password.

Once the user has verified their email, they would be manually vetted before being allowed to use the IntelOwl platform. The registration requests would be handled in the Django Admin page by admins. If you have IntelOwl deployed on an AWS instance with an IAM role you can use the SES service.

To have the \"Registration\" page to work correctly, you must configure some variables before starting IntelOwl. See Optional Environment Configuration

In a development environment the emails that would be sent are written to the standard output.

"},{"location":"IntelOwl/advanced_usage/#recaptcha-configuration","title":"Recaptcha configuration","text":"

The Registration Page contains a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown.

If your intention is to publish IntelOwl as a Service you should first remember to comply to the AGPL License.

Then you need to add the generated Recaptcha Secret in the RECAPTCHA_SECRET_KEY value in the env_file_app file.

Afterwards you should configure the Recaptcha Key for your site and add that value in the RECAPTCHA_SITEKEY in the frontend/public/env.js file. In that case, you would need to re-build the application to have the changes properly reflected.

"},{"location":"IntelOwl/advanced_usage/#optional-analyzers","title":"Optional Analyzers","text":"

Some analyzers which run in their own Docker containers are kept disabled by default. They are disabled by default to prevent accidentally starting too many containers and making your computer unresponsive.

Name Analyzers Description Malware Tools Analyzers
  • PEframe_Scan
  • Capa_Info
  • Floss
  • Strings_Info
  • ClamAV
  • APKiD
  • Thug_URL_Info, Thug_HTML_Info
  • BoxJS
  • Qiling_Windows, Qiling_Windows_Shellcode, Qiling_Linux, Qiling_Linux_Shellcode
  • PEFrame performs static analysis on Portable Executable malware and malicious MS Office documents
  • Capa detects capabilities in executable files
  • FLOSS automatically deobfuscate strings from malware binaries
  • String_Info_Classic extracts human-readable strings where as ML version of it ranks them
  • ClamAV antivirus engine scans files for trojans, viruses, malwares using a multi-threaded daemon
  • APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.
  • Thug performs hybrid dynamic/static analysis on a URL or HTML page.
  • Box-JS is a tool for studying JavaScript malware
  • Qiling is a tool for emulating the execution of a binary file or a shellcode. It requires the configuration of its rootfs, and the optional configuration of profiles. The rootfs can be copied from the Qiling project: please remember that Windows dll must be manually added for license reasons. Qiling provides a DllCollector to retrieve dlls from your licensed Windows. Profiles must be placed in the profiles subfolder
TOR Analyzers Onionscan Scans TOR .onion domains for privacy leaks and information disclosures. CyberChef CyberChef Run a transformation on a CyberChef server using pre-defined or custom recipes(rules that describe how the input has to be transformed). Check further instructions here PCAP Analyzers Suricata You can upload a PCAP to have it analyzed by Suricata with the open Ruleset. The result will provide a list of the triggered signatures plus a more detailed report with all the raw data generated by Suricata. You can also add your own rules (See paragraph \"Analyzers with special configuration\"). The installation is optimized for scaling so the execution time is really fast. PhoneInfoga PhoneInfoga_scan PhoneInfoga is one of the most advanced tools to scan international phone numbers. It allows you to first gather basic information such as country, area, carrier and line type, then use various techniques to try to find the VoIP provider or identify the owner. It works with a collection of scanners that must be configured in order for the tool to be effective. PhoneInfoga doesn't automate everything, it's just there to help investigating on phone numbers. here

To enable all the optional analyzers you can add the option --all_analyzers when starting the project. Example:

./start prod up --all_analyzers\n

Otherwise you can enable just one of the cited integration by using the related option. Example:

./start prod up --tor_analyzers\n
"},{"location":"IntelOwl/advanced_usage/#customize-analyzer-execution","title":"Customize analyzer execution","text":"

Some analyzers provide the chance to customize the performed analysis based on parameters that are different for each analyzer.

"},{"location":"IntelOwl/advanced_usage/#from-the-gui","title":"from the GUI","text":"

You can click on \"Runtime Configuration\" button in the \"Scan\" page and add the runtime configuration in the form of a dictionary. Example:

\"VirusTotal_v3_File\": {\n    \"force_active_scan_if_old\": true\n}\n
"},{"location":"IntelOwl/advanced_usage/#from-pyintelowl","title":"from Pyintelowl","text":"

While using send_observable_analysis_request or send_file_analysis_request endpoints, you can pass the parameter runtime_configuration with the optional values. Example:

runtime_configuration = {\n    \"Doc_Info\": {\n        \"additional_passwords_to_check\": [\"passwd\", \"2020\"]\n    }\n}\npyintelowl_client.send_file_analysis_request(..., runtime_configuration=runtime_configuration)\n
"},{"location":"IntelOwl/advanced_usage/#phoneinfoga","title":"PhoneInfoga","text":"

PhoneInfoga provides several Scanners to extract as much information as possible from a given phone number. Those scanners may require authentication, so they're automatically skipped when no authentication credentials are found.

By default the scanner used is local. Go through this guide to initiate other required API keys related to this analyzer.

"},{"location":"IntelOwl/advanced_usage/#cyberchef","title":"CyberChef","text":"

You can either use pre-defined recipes or create your own as explained here.

To use a pre-defined recipe, set the predefined_recipe_name argument to the name of the recipe as defined here. Else, leave the predefined_recipe_name argument empty and set the custom_recipe argument to the contents of the recipe you want to use.

Additionally, you can also (optionally) set the output_type argument.

"},{"location":"IntelOwl/advanced_usage/#pre-defined-recipes","title":"Pre-defined recipes","text":"
  • \"to decimal\": [{\"op\": \"To Decimal\", \"args\": [\"Space\", False]}]
"},{"location":"IntelOwl/advanced_usage/#analyzers-with-special-configuration","title":"Analyzers with special configuration","text":"

Some analyzers could require a special configuration:

  • GoogleWebRisk: this analyzer needs a service account key with the Google Cloud credentials to work properly. You should follow the official guide for creating the key. Then you can populate the secret service_account_json for that analyzer with the JSON of the service account file.

  • ClamAV: this Docker-based analyzer uses clamd daemon as its scanner and is communicating with clamdscan utility to scan files. The daemon requires 2 different configuration files: clamd.conf(daemon's config) and freshclam.conf (virus database updater's config). These files are mounted as docker volumes in /integrations/malware_tools_analyzers/clamav and hence, can be edited by the user as per needs, without restarting the application. Moreover ClamAV is integrated with unofficial open source signatures extracted with Fangfrisch. The configuration file fangfrisch.conf is mounted in the same directory and can be customized on your wish. For instance, you should change it if you want to integrate open source signatures from SecuriteInfo

  • Suricata: you can customize the behavior of Suricata:

  • /integrations/pcap_analyzers/config/suricata/rules: here there are Suricata rules. You can change the custom.rules files to add your own rules at any time. Once you made this change, you need to either restart IntelOwl or (this is faster) run a new analysis with the Suricata analyzer and set the parameter reload_rules to true.

  • /integrations/pcap_analyzers/config/suricata/etc: here there are Suricata configuration files. Change it based on your wish. Restart IntelOwl to see the changes applied.

  • Yara:

  • You can customize both the repositories parameter and private_repositories secret to download and use different rules from the default that IntelOwl currently support.
    • The repositories values is what will be used to actually run the analysis: if you have added private repositories, remember to add the url in repositories too!
  • You can add local rules inside the directory at /opt/deploy/files_required/yara/YOUR_USERNAME/custom_rules/. Please remember that these rules are not synced in a cluster deploy: for this reason is advised to upload them on GitHub and use the repositories or private_repositories attributes.
"},{"location":"IntelOwl/advanced_usage/#notifications","title":"Notifications","text":"

Since v4, IntelOwl integrated the notification system from the certego_saas package, allowing the admins to create notification that every user will be able to see.

The user would find the Notifications button on the top right of the page:

There the user can read notifications provided by either the administrators or the IntelOwl Maintainers.

As an Admin, if you want to add a notification to have it sent to all the users, you have to login to the Django Admin interface, go to the \"Notifications\" section and add it there. While adding a new notification, in the body section it is possible to even use HTML syntax, allowing to embed images, links, etc; in the app_name field, please remember to use intelowl as the app name.

Everytime a new release is installed, once the backend goes up it will automatically create a new notification, having as content the latest changes described in the CHANGELOG.md, allowing the users to keep track of the changes inside intelowl itself.

"},{"location":"IntelOwl/api_docs/","title":"API docs","text":""},{"location":"IntelOwl/contribute/","title":"Contribute","text":"

There are a lot of different ways you could choose to contribute to the IntelOwl Project:

  • main repository: IntelOwl

  • official Python client: pyintelowl.

  • official GO client: go-intelowl.

  • official IntelOwl Site: intelowlproject.github.io.

  • honeypots project: Greedybear

"},{"location":"IntelOwl/contribute/#rules","title":"Rules","text":"

Intel Owl welcomes contributors from anywhere and from any kind of education or skill level. We strive to create a community of developers that is welcoming, friendly and right.

For this reason it is important to follow some easy rules based on a simple but important concept: Respect.

  • Before asking any questions regarding how the project works, please read through all the documentation and install the project on your own local machine to try it and understand how it basically works. This is a form of respect to the maintainers.

  • DO NOT contact the maintainers with direct messages unless it is an urgent request. We don't have much time and cannot just answer to all the questions that we receive like \"Guide me please! Help me understand how the project work\". There is plenty of documentation and a lot of people in the community that can help you and would benefit from your questions. Share your problems and your knowledge. Please ask your questions in open channels (Github and Slack). This is a form of respect to the maintainers and to the community.

  • Before starting to work on an issue, you need to get the approval of one of the maintainers. Therefore please ask to be assigned to an issue. If you do not that but you still raise a PR for that issue, your PR can be rejected. This is a form of respect for both the maintainers and the other contributors who could have already started to work on the same problem.

  • When you ask to be assigned to an issue, it means that you are ready to work on it. When you get assigned, take the lock and then you disappear, you are not respecting the maintainers and the other contributors who could be able to work on that. So, after having been assigned, you have a week of time to deliver your first draft PR. After that time has passed without any notice, you will be unassigned.

  • Once you started working on an issue and you have some work to share and discuss with us, please raise a draft PR early with incomplete changes. This way you can continue working on the same and we can track your progress and actively review and help. This is a form of respect to you and to the maintainers.

  • When creating a PR, please read through the sections that you will find in the PR template and compile it appropriately. If you do not, your PR can be rejected. This is a form of respect to the maintainers.

"},{"location":"IntelOwl/contribute/#code-style","title":"Code Style","text":"

Keeping to a consistent code style throughout the project makes it easier to contribute and collaborate. We make use of psf/black and isort for code formatting and flake8 for style guides.

"},{"location":"IntelOwl/contribute/#how-to-start-setup-project-and-development-instance","title":"How to start (Setup project and development instance)","text":"

This guide assumes that you have already performed the steps required to install the project. If not, please do it (Installation Guide).

Create a personal fork of the project on Github. Then, please create a new branch based on the develop branch that contains the most recent changes. This is mandatory.

git checkout -b myfeature develop

Then we strongly suggest to configure pre-commit to force linters on every commits you perform

# From the project directory\npython3 -m venv venv\nsource venv/bin/activate\n# from the project base directory\npip install pre-commit\npre-commit install\n\n# create .env file for controlling repo_downloader.sh\n# (to speed up image builds during development: it avoid downloading some repos)\ncp docker/.env.start.test.template docker/.env.start.test\n\n# set STAGE env variable to \"local\"\nsed -i \"s/STAGE=\\\"production\\\"/STAGE=\\\"local\\\"/g\" docker/env_file_app\n
"},{"location":"IntelOwl/contribute/#backend","title":"Backend","text":"

Now, you can execute IntelOwl in development mode by selecting the mode test while launching the startup script:

./start test up\n

Every time you perform a change, you should perform an operation to reflect the changes into the application:

  • if you changed the python requirements, restart the application and re-build the images. This is the slowest process. You can always choose this way but it would waste a lot of time.
./start test down && ./start test up -- --build\n
  • if you changed either analyzers, connectors, playbooks or anything that is executed asynchronously by the \"celery\" containers, you just need to restart the application because we leverage Docker bind volumes that will reflect the changes to the containers. This saves the time of the build
./start test down && ./start test up\n
  • if you made changes to either the API or anything that is executed only by the application server, changes will be instantly reflected and you don't need to do anything. This is thanks to the Django Development server that is executed instead of uwsgi while using the test mode
"},{"location":"IntelOwl/contribute/#note-about-documentation","title":"NOTE about documentation:","text":"

If you made any changes to an existing model/serializer/view, please run the following command to generate a new version of the API schema and docs:

docker exec -it intelowl_uwsgi python manage.py spectacular --file docs/source/schema.yml && make html\n
"},{"location":"IntelOwl/contribute/#frontend","title":"Frontend","text":"

To start the frontend in \"develop\" mode, you can execute the startup npm script within the folder frontend:

cd frontend/\n# Install\nnpm i\n# Start\nDANGEROUSLY_DISABLE_HOST_CHECK=true npm start\n# See https://create-react-app.dev/docs/proxying-api-requests-in-development/#invalid-host-header-errors-after-configuring-proxy for why we use that flag in development mode\n

Most of the time you would need to test the changes you made together with the backend. In that case, you would need to run the backend locally too:

./start prod up\n

Note

  • Running prod would be faster because you would leverage the official images and you won't need to build the backend locally. In case you would need to test backend changes too at the same time, please use test and refer to the previous section of the documentation.
  • This works thanks to the directive proxy in the frontend/package.json configuration
  • It may happen that the backend build does not work due to incompatibility between the frontend version you are testing with the current complete IntelOwl version you are running. In those cases, considering that you don't need to build the frontend together with the backend because you are already testing it separately, we suggest to remove the first build step (the frontend part) from the main Dockerfile temporarily and build IntelOwl with only the backend. In this way there won't be conflict issues.
"},{"location":"IntelOwl/contribute/#certego-ui","title":"Certego-UI","text":"

The IntelOwl Frontend is tightly linked to the certego-ui library. Most of the React components are imported from there. Because of this, it may happen that, during development, you would need to work on that library too. To install the certego-ui library, please take a look to npm link and remember to start certego-ui without installing peer dependencies (to avoid conflicts with IntelOwl dependencies):

git clone https://github.com/certego/certego-ui.git\n# change directory to the folder where you have the cloned the library\ncd certego-ui/\n# install, without peer deps (to use packages of IntelOwl)\nnpm i --legacy-peer-deps\n# create link to the project (this will globally install this package)\nsudo npm link\n# compile the library\nnpm start\n

Then, open another command line tab, create a link in the frontend to the certego-ui and re-install and re-start the frontend application (see previous section):

cd frontend/\nnpm link @certego/certego-ui\n

This trick will allow you to see reflected every changes you make in the certego-ui directly in the running frontend application.

"},{"location":"IntelOwl/contribute/#example-application","title":"Example application","text":"

The certego-ui application comes with an example project that showcases the components that you can re-use and import to other projects, like IntelOwl:

# To have the Example application working correctly, be sure to have installed `certego-ui` *without* the `--legacy-peer-deps` option and having it started in another command line\ncd certego-ui/\nnpm i\nnpm start\n# go to another tab\ncd certego-ui/example/\nnpm i\nnpm start\n
"},{"location":"IntelOwl/contribute/#how-to-add-a-new-plugin","title":"How to add a new Plugin","text":"

IntelOwl was designed to ease the addition of new plugins. With a simple python script you can integrate your own engine or integrate an external service in a short time.

There are two possible cases:

  1. You are creating an entirely new Plugin, meaning that you actually wrote python code
  2. You are creating a new Configuration for some code that already exists.

If you are doing the step number 2, you can skip this paragraph.

First, you need to create the python code that will be actually executed. You can easily take other plugins as example to write this. Then, you have to create a Python Module model. You can do this in the Django Admin page: You have to specify which type of Plugin you wrote, and its python module. Again, you can use as an example an already configured Python Module.

Some Python Module requires to update some part of its code in a schedule way: for example Yara requires to update the rule repositories, QuarkEngine to update its database and so on. If the Python Module that you define need this type of behaviour, you have to configure two things:

  • In the python code, you have to override a method called update and put the updating logic (see other plugins for examples) there.
  • In the model class, you have to add the update_schedule (crontab syntax) that define when the update should be executed.

Some Python Module requires further check to see if the service provider is able to answer requests; for example if you have done too many requests, or the website is currently down for maintenance and so on. If the Python Module that you define need this type of behaviour, you have to configure two things:

  • In the python code, you can override a method called health_check and put there the custom health check logic. As default, plugins will try to make an HTTP HEAD request to the configured url (the Plugin must have a url attribute).
  • In the model class, you have to add the health_check_schedule (crontab syntax) that define when the health check should be executed.

Press Save and continue editing to, at the moment, manually ad the Parameters that the python code requires (the class attributes that you needed):

  1. *name: Name of the parameter that will be dynamically added to the python class (if is a secret, in the python code a _ wil be prepended to the name)
  2. *type: data type, string, list, dict, integer, boolean, float
  3. *description
  4. *required: true or false, meaning that a value is necessary to allow the run of the analyzer
  5. *is_secret: true or false

At this point, you can follow the specific guide for each plugin

"},{"location":"IntelOwl/contribute/#how-to-add-a-new-analyzer","title":"How to add a new Analyzer","text":"

You may want to look at a few existing examples to start to build a new one, such as:

  • shodan.py, if you are creating an observable analyzer
  • malpedia_scan.py, if you are creating a file analyzer
  • peframe.py, if you are creating a docker based analyzer
  • Please note: If the new analyzer that you are adding is free for the user to use, please add it in the FREE_TO_USE_ANALYZERS playbook. To do this you have to make a migration file; you can use 0026_add_mmdb_analyzer_free_to_use as a template.

After having written the new python module, you have to remember to:

  1. Put the module in the file_analyzers or observable_analyzers directory based on what it can analyze
  2. Remember to use _monkeypatch() in its class to create automated tests for the new analyzer. This is a trick to have tests in the same class of its analyzer.
  3. Create the configuration inside django admin in Analyzers_manager/AnalyzerConfigs (* = mandatory, ~ = mandatory on conditions)
  4. *Name: specific name of the configuration
  5. *Python module: .
  6. *Description: description of the configuration
  7. *Routing key: celery queue that will be used
  8. *Soft_time_limit: maximum time for the task execution
  9. *Type: observable or file
  10. *Docker based: if the analyzer run through a docker instance
  11. *Maximum tlp: maximum tlp to allow the run on the connector
  12. ~Observable supported: required if type is observable
  13. ~Supported filetypes: required if type is file and not supported filetypes is empty
  14. Run hash: if the analyzer supports hash as inputs
  15. ~Run hash type: required if run hash is True
  16. ~Not supported filetypes: required if type is file and supported filetypes is empty
  17. "},{"location":"IntelOwl/contribute/#integrating-a-docker-based-analyzer","title":"Integrating a docker based analyzer","text":"

    If the analyzer you wish to integrate doesn't exist as a public API or python package, it should be integrated with its own docker image which can be queried from the main Django app.

    • It should follow the same design principle as the other such existing integrations, unless there's very good reason not to.
    • The dockerfile should be placed at ./integrations/<analyzer_name>/Dockerfile.
    • Two docker-compose files compose.yml for production and compose-tests.yml for testing should be placed under ./integrations/<analyzer_name>.
    • If your docker-image uses any environment variables, add them in the docker/env_file_integrations_template.
    • Rest of the steps remain same as given under \"How to add a new analyzer\".
    "},{"location":"IntelOwl/contribute/#how-to-add-a-new-connector","title":"How to add a new Connector","text":"

    You may want to look at a few existing examples to start to build a new one:

    • misp.py
    • opencti.py

    After having written the new python module, you have to remember to:

    1. Put the module in the connectors directory
    2. Remember to use _monkeypatch() in its class to create automated tests for the new connector. This is a trick to have tests in the same class of its connector.
    3. Create the configuration inside django admin in Connectors_manager/ConnectorConfigs (* = mandatory, ~ = mandatory on conditions)
    4. *Name: specific name of the configuration
    5. *Python module: .
    6. *Description: description of the configuration
    7. *Routing key: celery queue that will be used
    8. *Soft_time_limit: maximum time for the task execution
    9. *Maximum tlp: maximum tlp to allow the run on the connector
    10. *Run on failure: if the connector should be run even if the job fails
    11. "},{"location":"IntelOwl/contribute/#how-to-add-a-new-ingestor","title":"How to add a new Ingestor","text":"
      1. Put the module in the ingestors directory
      2. Remember to use _monkeypatch() in its class to create automated tests for the new ingestor. This is a trick to have tests in the same class of its ingestor.
      3. Create the configuration inside django admin in Ingestors_manager/IngestorConfigs (* = mandatory, ~ = mandatory on conditions)
      4. *Name: specific name of the configuration
      5. *Python module: .
      6. *Description: description of the configuration
      7. *Routing key: celery queue that will be used
      8. *Soft_time_limit: maximum time for the task execution
      9. *Playbook to Execute: Playbook that will be executed on every IOC retrieved
      10. *Schedule: Crontab object that describes the schedule of the ingestor. You are able to create a new clicking the plus symbol.
      11. "},{"location":"IntelOwl/contribute/#how-to-add-a-new-pivot","title":"How to add a new Pivot","text":"
        1. Put the module in the pivots directory
        2. Remember to use _monkeypatch() in its class to create automated tests for the new pivot. This is a trick to have tests in the same class of its pivot.
        3. Create the configuration inside django admin in Pivots_manager/PivotConfigs (* = mandatory, ~ = mandatory on conditions)
        4. *Name: specific name of the configuration
        5. *Python module: .
        6. *Description: description of the configuration
        7. *Routing key: celery queue that will be used
        8. *Soft_time_limit: maximum time for the task execution
        9. *Playbook to Execute: Playbook that will be executed in the Job generated by the Pivot
        10. Most of the times you don't need to create a new Pivot Module. There are already some base modules that can be extended. The most important ones are the following 2: 1.AnyCompare: use this module if you want to create a custom Pivot from a specific value extracted from the results of the analyzers/connectors. How? you should populate the parameter field_to_compare with the dotted path to the field you would like to extract the value from. 2.SelfAnalyzable: use this module if you want to create a custom Pivot that would analyze again the same observable/file.

          "},{"location":"IntelOwl/contribute/#how-to-add-a-new-visualizer","title":"How to add a new Visualizer","text":""},{"location":"IntelOwl/contribute/#configuration","title":"Configuration","text":"
          1. Put the module in the visualizers directory
          2. Remember to use _monkeypatch() in its class to create automated tests for the new visualizer. This is a trick to have tests in the same class of its visualizer.
          3. Create the configuration inside django admin in Visualizers_manager/VisualizerConfigs (* = mandatory, ~ = mandatory on conditions)
          4. *Name: specific name of the configuration
          5. *Python module: .
          6. *Description: description of the configuration
          7. *Config:
            1. *Queue: celery queue that will be used
            2. *Soft_time_limit: maximum time for the task execution
          8. *Playbook: Playbook that must have run to execute the visualizer
          9. "},{"location":"IntelOwl/contribute/#python-class","title":"Python class","text":"

            The visualizers' python code could be not immediate, so a small digression on how it works is necessary. Visualizers have as goal to create a data structure inside the Report that the frontend is able to parse and correctly visualize on the page. To do so, some utility classes have been made:

            Class Description Visual representation/example VisualizablePage A single page of the final report, made of different levels. Each page added is represented as a new tab in frontend. VisualizableLevel Each level corresponds to a line in the final frontend visualizations. Every level is made of a VisualizableHorizontalList. The dimension of the level can be customized with the size parameter (1 is the biggest, 6 is the smallest). VisualizableHorizontalList An horizontal list of visualizable elements. In the example there is an horizontal list of vertical lists. VisualizableVerticalList A vertical list made of a name, a title, and the list of elements. VisualizableTable A table of visualizable elements. In the example there is a table of base and vertical lists. VisualizableBool The representation of a boolean value. It can be enabled or disabled with colors. VisualizableTitle The representation of a tuple, composed of a title and a value. VisualizableBase The representation of a base string. Can have a link attached to it and even an icon. The background color can be changed. The title above is composed by two `VisualizableBase`

            Inside a Visualizer you can retrieve the reports of the analyzers and connectors that have been specified inside configuration of the Visualizer itself using .analyzer_reports() and .connector_reports(). At this point, you can compose these values as you wish wrapping them with the Visualizable classes mentioned before.

            The best way to create a visualizer is to define several methods, one for each Visualizable you want to show in the UI, in your new visualizer and decore them with visualizable_error_handler_with_params. This decorator handles exceptions: in case there is a bug during the generation of a Visualizable element, it will be show an error instead of this component and all the other Visualizable are safe and will render correctly. Be careful using it because is a function returning a decorator! This means you need to use a syntax like this:

            @visualizable_error_handler_with_params(error_name=\"custom visualizable\", error_size=VisualizableSize.S_2)\ndef custom_visualizable(self):\n   ...\n

            instead of the syntax of other decorators that doesn't need the function call.

            You may want to look at a few existing examples to start to build a new one:

            • dns.py
            • yara.py
            "},{"location":"IntelOwl/contribute/#how-to-share-your-plugin-with-the-community","title":"How to share your plugin with the community","text":"

            To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration

            1. You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/YOUR_PLUGIN_manager/migrations). The script will create the following models:
              1. PythonModule
              2. AnalyzerConfig
              3. Parameter
              4. PluginConfig
            2. Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin AnalyzerConfig <new_analyzer_name>

            Add the new analyzer in the lists in the docs: Usage. Also, if the analyzer provides additional optional configuration, add the available options here: Advanced-Usage

            In the Pull Request remember to provide some real world examples (screenshots and raw JSON results) of some successful executions of the analyzer to let us understand how it would work.

            "},{"location":"IntelOwl/contribute/#how-to-add-a-new-playbook","title":"How to add a new Playbook","text":"
            1. Create the configuration inside django admin in Playbooks_manager/PlaybookConfigs (* = mandatory, ~ = mandatory on conditions)
            2. *Name: specific name of the configuration
            3. *Description: description of the configuration
            4. *Type: list of types that are supported by the playbook
            5. *Analyzers: List of analyzers that will be run
            6. *Connectors: List of connectors that will be run
            "},{"location":"IntelOwl/contribute/#how-to-share-your-playbook-with-the-community","title":"How to share your playbook with the community","text":"

            To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/playbook_manager/migrations).

            Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin PlaybookConfig <new_analyzer_name>

            "},{"location":"IntelOwl/contribute/#how-to-modify-a-plugin","title":"How to modify a plugin","text":"

            If the changes that you have to make should stay local, you can just change the configuration inside the Django admin page.

            But if, instead, you want your changes to be usable by every IntelOwl user, you have to create a new migration.

            To do so, you can use the following snippets as an example:

            1. You have to create a new migration file
            2. Add as dependency the previous last migration of the package
            3. You have to create a forward and a reverse function
            4. You have to make the proper changes of the configuration inside these functions (change parameters, secrets, or even delete the configuration)
            5. If changes are made, you have to validate the instance calling .full_clean() and then you can save the instance with .save()
            "},{"location":"IntelOwl/contribute/#example-how-to-add-a-new-parameter-in-the-configuration-with-a-default-value","title":"Example: how to add a new parameter in the configuration with a default value","text":"
            def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   PluginConfig = apps.get_model(\"api_app\", \"PluginConfig\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   p = Parameter(name=\"mynewfield\", type=\"str\", description=\"Test field\", is_secret=False, required=True, python_module=pm)\n   p.full_clean()\n   p.save()\n   for connector in pm.connectorconfigs.all():\n    pc = PluginConfig(value=\"test\", connector_config=connector, python_module=pm, for_organization=False, owner=None, parameter=p)\n    pc.full_clean()\n    pc.save()\n
            "},{"location":"IntelOwl/contribute/#example-how-to-add-a-new-secret-in-the-configuration","title":"Example: how to add a new secret in the configuration","text":"
            def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   p = Parameter(name=\"mynewsecret\", type=\"str\", description=\"Test field\", is_secret=True, required=True, python_module=pm)\n   p.full_clean()\n   p.save()\n
            "},{"location":"IntelOwl/contribute/#example-how-to-delete-a-parameter","title":"Example: how to delete a parameter","text":"
            def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   Parameter.objects.get(name=\"myoldfield\", python_module=pm).delete()\n
            "},{"location":"IntelOwl/contribute/#example-how-to-change-the-default-value-of-a-parameter","title":"Example: how to change the default value of a parameter","text":"
            def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   PluginConfig = apps.get_model(\"api_app\", \"PluginConfig\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   p = Parameter.objects.get(name=\"myfield\", python_module=pm)\n   PluginConfig.objects.filter(parameter=p, python_module=pm, for_organization=False, owner=None ).update(value=\"newvalue\")\n
            "},{"location":"IntelOwl/contribute/#modifying-functionalities-of-the-certego-packages","title":"Modifying functionalities of the Certego packages","text":"

            Since v4, IntelOwl leverages some packages from Certego:

            • certego-saas that integrates some common reusable Django applications and tools that can be used for generic services.
            • certego-ui that contains reusable React components for the UI.

            If you need to modify the behavior or add feature to those packages, please follow the same rules for IntelOwl and request a Pull Request there. The same maintainers of IntelOwl will answer to you.

            Follow these guides to understand how to start to contribute to them while developing for IntelOwl:

            • certego-saas: create a fork, commit your changes in your local repo, then change the commit hash to the last one you made in the requirements file. Ultimately re-build the project
            • certego-ui: Frontend doc
            "},{"location":"IntelOwl/contribute/#how-to-test-the-application","title":"How to test the application","text":"

            IntelOwl makes use of the django testing framework and the unittest library for unit testing of the API endpoints and End-to-End testing of the analyzers and connectors.

            "},{"location":"IntelOwl/contribute/#configuration_1","title":"Configuration","text":"
            • In the encrypted folder tests/test_files.zip (password: \"intelowl\") there are some files that you can use for testing purposes.

            • With the following environment variables you can customize your tests:

            • DISABLE_LOGGING_TEST -> disable logging to get a clear output

            • MOCK_CONNECTIONS -> mock connections to external API to test the analyzers without a real connection or a valid API key

            • If you prefer to use custom inputs for tests, you can change the following environment variables in the environment file based on the data you would like to test:

            • TEST_MD5
            • TEST_URL
            • TEST_IP
            • TEST_DOMAIN
            "},{"location":"IntelOwl/contribute/#setup-containers","title":"Setup containers","text":"

            The point here is to launch the code in your environment and not the last official image in Docker Hub. For this, use the test or the ci option when launching the containers with the ./start script.

            • Use the test option to actually execute tests that simulate a real world environment without mocking connections.
            • Use the ci option to execute tests in a CI environment where connections are mocked.
            $ ./start test up\n$ # which corresponds to the command: docker-compose -f docker/default.yml -f docker/test.override.yml up\n
            "},{"location":"IntelOwl/contribute/#launch-tests","title":"Launch tests","text":"

            Now that the containers are up, we can launch the test suite.

            "},{"location":"IntelOwl/contribute/#backend_1","title":"Backend","text":""},{"location":"IntelOwl/contribute/#run-all-tests","title":"Run all tests","text":"

            Examples:

            $ docker exec intelowl_uwsgi python3 manage.py test\n
            "},{"location":"IntelOwl/contribute/#run-tests-for-a-particular-plugin","title":"Run tests for a particular plugin","text":"

            To test a plugin in real environment, i.e. without mocked data, we suggest that you use the GUI of IntelOwl directly. Meaning that you have your plugin configured, you have selected a correct observable/file to analyze, and the final report shown in the GUI of IntelOwl is exactly what you wanted.

            "},{"location":"IntelOwl/contribute/#run-tests-available-in-a-particular-file","title":"Run tests available in a particular file","text":"

            Examples:

            $ docker exec intelowl_uwsgi python3 manage.py test tests.api_app tests.test_crons # dotted paths\n
            "},{"location":"IntelOwl/contribute/#frontend_1","title":"Frontend","text":"

            All the frontend tests must be run from the folder frontend. The tests can contain log messages, you can suppress then with the environment variable SUPPRESS_JEST_LOG=True.

            "},{"location":"IntelOwl/contribute/#run-all-tests_1","title":"Run all tests","text":"
            npm test\n
            "},{"location":"IntelOwl/contribute/#run-a-specific-component-tests","title":"Run a specific component tests","text":"
            npm test -- -t <componentPath>\n// example\nnpm test tests/components/auth/Login.test.jsx\n
            "},{"location":"IntelOwl/contribute/#run-a-specific-test","title":"Run a specific test","text":"
            npm test -- -t '<describeString> <testString>'\n// example\nnpm test -- -t \"Login component User login\"\n
            "},{"location":"IntelOwl/contribute/#create-a-pull-request","title":"Create a pull request","text":""},{"location":"IntelOwl/contribute/#remember","title":"Remember!!!","text":"

            Please create pull requests only for the branch develop. That code will be pushed to master only on a new release.

            Also remember to pull the most recent changes available in the develop branch before submitting your PR. If your PR has merge conflicts caused by this behavior, it won't be accepted.

            "},{"location":"IntelOwl/contribute/#install-testing-requirements","title":"Install testing requirements","text":"

            Run pip install -r requirements/test-requirements.txt to install the requirements to validate your code.

            "},{"location":"IntelOwl/contribute/#pass-linting-and-tests","title":"Pass linting and tests","text":"
            1. Run psf/black to lint the files automatically, then flake8 to check and isort:

            (if you installed pre-commit this is performed automatically at every commit)

            $ black . --exclude \"migrations|venv\"\n$ flake8 . --show-source --statistics\n$ isort . --profile black --filter-files --skip venv\n

            if flake8 shows any errors, fix them.

            1. Run the build and start the app using the docker-compose test file. In this way, you would launch the code in your environment and not the last official image in Docker Hub:
            $ ./start ci build\n$ ./start ci up\n
            1. Here, we simulate the GitHub CI tests locally by running the following 3 tests:
            $ docker exec -ti intelowl_uwsgi unzip -P intelowl tests/test_files.zip -d test_files\n$ docker exec -ti intelowl_uwsgi python manage.py test tests\n

            Note: IntelOwl has dynamic testing suite. This means that no explicit analyzers/connector tests are required after the addition of a new analyzer or connector.

            If everything is working, before submitting your pull request, please squash your commits into a single one!

            "},{"location":"IntelOwl/contribute/#how-to-squash-commits-to-a-single-one","title":"How to squash commits to a single one","text":"
            • Run git rebase -i HEAD~[NUMBER OF COMMITS]
            • You should see a list of commits, each commit starting with the word \"pick\".
            • Make sure the first commit says \"pick\" and change the rest from \"pick\" to \"squash\". -- This will squash each commit into the previous commit, which will continue until every commit is squashed into the first commit.
            • Save and close the editor.
            • It will give you the opportunity to change the commit message.
            • Save and close the editor again.
            • Then you have to force push the final, squashed commit: git push --force-with-lease origin.

            Squashing commits can be a tricky process but once you figure it out, it's really helpful and keeps our repo concise and clean.

            "},{"location":"IntelOwl/contribute/#debug-application-problems","title":"Debug application problems","text":"

            Keep in mind that, if any errors arise during development, you would need to check the application logs to better understand what is happening so you can easily address the problem.

            This is the reason why it is important to add tons of logs in the application...if they are not available in time of needs you would cry really a lot.

            Where are IntelOwl logs? With a default installation of IntelOwl, you would be able to get the application data from the following paths in your OS:

            • /var/lib/docker/volumes/intel_owl_generic_logs/_data/django: Django Application logs
            • /var/lib/docker/volumes/intel_owl_generic_logs/_data/uwsgi: Uwsgi application server logs
            • /var/lib/docker/volumes/intel_owl_nginx_logs/_data/: Nginx Web Server Logs
            "},{"location":"IntelOwl/installation/","title":"Installation","text":""},{"location":"IntelOwl/installation/#requirements","title":"Requirements","text":"

            The project leverages docker compose with a custom Bash script and you need to have the following packages installed in your machine:

            • docker - v19.03.0+
            • docker-compose - v2.3.4+

            In some systems you could find pre-installed older versions. Please check this and install a supported version before attempting the installation. Otherwise it would fail. Note: We've added a new Bash script initialize.sh that will check compatibility with your system and attempt to install the required dependencies.

            Note

            • The project uses public docker images that are available on Docker Hub
            • IntelOwl is tested and supported to work in a Debian distro. More precisely we suggest using Ubuntu. Other Linux-based OS should work but that has not been tested much. It may also run on Windows, but that is not officially supported.
            • IntelOwl does not support ARM at the moment. We'll fix this with the next v6.0.5 release
            • Before installing remember that you must comply with the LICENSE and the Legal Terms
            • Warning

              The start script requires a `bash` version > 4 to run. Note that macOS is shipped with an older version of bash. Please ensure to upgrade before running the script."},{"location":"IntelOwl/installation/#tldr","title":"TL;DR","text":"

              Obviously we strongly suggest reading through all the page to configure IntelOwl in the most appropriate way.

              However, if you feel lazy, you could just install and test IntelOwl with the following steps. docker will be run with sudo if permissions/roles have not been set.

              # clone the IntelOwl project repository\ngit clone https://github.com/intelowlproject/IntelOwl\ncd IntelOwl/\n\n# run helper script to verify installed dependencies and configure basic stuff\n./initialize.sh\n\n# start the app\n./start prod up\n# now the application is running on http://localhost:80\n\n# create a super user\nsudo docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser\n\n# now you can login with the created user from http://localhost:80/login\n\n# Have fun!\n

              Warning

              The first time you start IntelOwl, a lot of database migrations are being applied. This requires some time. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page."},{"location":"IntelOwl/installation/#infrastructure-requirements","title":"Infrastructure Requirements","text":"

              These are our recommendations for dedicated deployments of IntelOwl: - Basic Installation in a VM: 2 CPU, 4GB RAM, 20GB Disk - Intensive Usage (hundreds of analysis in a hour) in a single VM: 8CPU, 16GB RAM and 80GB Disk.

              Please remember that every environment has its own peculiarities so these numbers must not be taken as the holy grail.

              What should be done is a comprehensive evaluation of the environment where the application will deployed.

              For more complex environments, a Docker Swarm / Kubernetes cluster is recommended.

              IntelOwl's maintainers are available to offer paid consultancy and mentorship about that.

              "},{"location":"IntelOwl/installation/#deployment-components","title":"Deployment Components","text":"

              IntelOwl is composed of various different technologies, namely:

              • React: Frontend, using CRA and certego-ui
              • Django: Backend
              • PostgreSQL: Database
              • Redis: Message Broker
              • Celery: Task Queue
              • Nginx: Reverse proxy for the Django API and web asssets.
              • Uwsgi: Application Server
              • Daphne: Asgi Server for WebSockets
              • Elastic Search (optional): Auto-sync indexing of analysis' results.
              • Kibana (optional): GUI for Elastic Search. We provide a saved configuration with dashboards and visualizations.
              • Flower (optional): Celery Management Web Interface

              All these components are managed via docker compose.

              "},{"location":"IntelOwl/installation/#deployment-preparation","title":"Deployment Preparation","text":"
              • Environment configuration (required)
              • Database configuration (required)
              • Web server configuration (optional)
              • Analyzers configuration (optional)

              Open a terminal and execute below commands to construct new environment files from provided templates.

              ./initialize.sh\n
              "},{"location":"IntelOwl/installation/#environment-configuration-required","title":"Environment configuration (required)","text":"

              In the docker/env_file_app, configure different variables as explained below.

              REQUIRED variables to run the image:

              • DB_HOST, DB_PORT, DB_USER, DB_PASSWORD: PostgreSQL configuration (The DB credentals should match the ones in the env_file_postgres). If you like, you can configure the connection to an external PostgreSQL instance in the same variables. Then, to avoid to run PostgreSQL locally, please run IntelOwl with the option --use-external-database. Otherwise, DB_HOST must be postgres to have the app properly communicate with the PostgreSQL container.
              • DJANGO_SECRET: random 50 chars key, must be unique. If you do not provide one, Intel Owl will automatically set a secret key and use the same for each run. The key is generated by initialize.sh script.

              Strongly recommended variable to set:

              • INTELOWL_WEB_CLIENT_DOMAIN (example: localhost/mywebsite.com): the web domain of your instance, this is used for generating links to analysis results.

              Optional configuration:

              • OLD_JOBS_RETENTION_DAYS: Database retention for analysis results (default: 14 days). Change this if you want to keep your old analysis longer in the database.
              "},{"location":"IntelOwl/installation/#other-optional-configuration-to-enable-specific-services-features","title":"Other optional configuration to enable specific services / features","text":"

              Configuration required to enable integration with Slack:

              • SLACK_TOKEN: Slack token of your Slack application that will be used to send/receive notifications
              • DEFAULT_SLACK_CHANNEL: ID of the Slack channel you want to post the message to

              Configuration required to enable Re-Captcha in the Login and the Registration Page: In the docker/env_file_app:

              • USE_RECAPTCHA: if you want to use recaptcha on your login
              • RECAPTCHA_SECRET_KEY: your recaptcha secret key In the frontend/public/env.js:
              • RECAPTCHA_SITEKEY: Recaptcha Key for your site

              Configuration required to have InteOwl sending Emails (registration requests, mail verification, password reset/change, etc)

              • DEFAULT_FROM_EMAIL: email address used for automated correspondence from the site manager (example: noreply@mydomain.com)
              • DEFAULT_EMAIL: email address used for correspondence with users (example: info@mydomain.com)
              • EMAIL_HOST: the host to use for sending email with SMTP
              • EMAIL_HOST_USER: username to use for the SMTP server defined in EMAIL_HOST
              • EMAIL_HOST_PASSWORD: password to use for the SMTP server defined in EMAIL_HOST. This setting is used in conjunction with EMAIL_HOST_USER when authenticating to the SMTP server.
              • EMAIL_PORT: port to use for the SMTP server defined in EMAIL_HOST.
              • EMAIL_USE_TLS: whether to use an explicit TLS (secure) connection when talking to the SMTP server, generally used on port 587.
              • EMAIL_USE_SSL: whether to use an implicit TLS (secure) connection when talking to the SMTP server, generally used on port 465.
              "},{"location":"IntelOwl/installation/#database-configuration-required-if-running-postgresql-locally","title":"Database configuration (required if running PostgreSQL locally)","text":"

              If you use a local PostgreSQL instance (this is the default), in the env_file_postgres you have to configure different variables as explained below.

              Required variables:

              • POSTGRES_PASSWORD (same as DB_PASSWORD)
              • POSTGRES_USER (same as DB_USER)
              • POSTGRES_DB (default: intel_owl_db)
              "},{"location":"IntelOwl/installation/#logrotate-configuration-strongly-recommended","title":"Logrotate configuration (strongly recommended)","text":"

              If you want to have your logs rotated correctly, we suggest you to add the configuration for the system Logrotate. To do that you can leverage the initialize.sh script. Otherwise, if you have skipped that part, you can manually install logrotate by launching the following script:

              cd ./docker/scripts\n./install_logrotate.sh\n

              We decided to do not leverage Django Rotation Configuration because it caused problematic concurrency issues, leading to logs that are not rotated correctly and to apps that do not log anymore. Logrotate configuration is more stable.

              "},{"location":"IntelOwl/installation/#crontab-configuration-recommended-for-advanced-deployments","title":"Crontab configuration (recommended for advanced deployments)","text":"

              We added few Crontab configurations that could be installed in the host machine at system level to solve some possible edge-case issues:

              • Memory leaks: Once a week it is suggested to do a full restart of the application to clean-up the memory used by the application. Practical experience suggest us to do that to solve some recurrent memory issues in Celery. A cron called application_restart has been added for this purpose (it uses the absolute path of start script in the container). This cron assumes that you have executed IntelOwl with the parameters --all_analyzers. If you didn't, feel free to change the cron as you wish.

              This configuration is optional but strongly recommended for people who want to have a production grade deployment. To install it you need to run the following script in each deployed server:

              cd ./docker/scripts\n./install_crontab.sh\n
              "},{"location":"IntelOwl/installation/#web-server-configuration-required-for-enabling-https","title":"Web server configuration (required for enabling HTTPS)","text":"

              Intel Owl provides basic configuration for:

              • Nginx (configuration/nginx/http.conf)
              • Uwsgi (configuration/intel_owl.ini)

              In case you enable HTTPS, remember to set the environment variable HTTPS_ENABLED as \"enabled\" to increment the security of the application.

              There are 3 options to execute the web server:

              • HTTP only (default)

              The project would use the default deployment configuration and HTTP only.

              • HTTPS with your own certificate

              The project provides a template file to configure Nginx to serve HTTPS: configuration/nginx/https.conf.

              You should change ssl_certificate, ssl_certificate_key and server_name in that file and put those required files in the specified locations.

              Then you should call the ./start script with the parameter --https to leverage the right Docker Compose file for HTTPS.

              Plus, if you use Flower, you should change in the docker/flower.override.yml the flower_http.conf with flower_https.conf.

              • HTTPS with Let's Encrypt

              We provide a specific docker-compose file that leverages Traefik to allow fast deployments of public-faced and HTTPS-enabled applications.

              Before using it, you should configure the configuration file docker/traefik.override.yml by changing the email address and the hostname where the application is served. For a detailed explanation follow the official documentation: Traefix doc.

              After the configuration is done, you can add the option --traefik while executing ./start

              "},{"location":"IntelOwl/installation/#run","title":"Run","text":"

              Important Info

              IntelOwl depends heavily on docker and docker compose so as to hide this complexity from the enduser the project leverages a custom shell script (start) to interface with docker compose. You may invoke $ ./start --help to get help and usage info. The CLI provides the primitives to correctly build, run or stop the containers for IntelOwl. Therefore,
              • It is possible to attach every optional docker container that IntelOwl has: multi_queue with traefik enabled while every optional docker analyzer is active.
              • It is possible to insert an optional docker argument that the CLI will pass to docker-compose

              Now that you have completed different configurations, starting the containers is as simple as invoking:

              $ ./start prod up\n

              You can add the docker options -d to run the application in the background.

              Important Info

              All docker and docker compose specific options must be passed at the end of the script, after a -- token. This token indicates the end of IntelOwl's options and the beginning of Docker options. Example:
              ./start prod up -- -d\n

              Hint

              Starting from IntelOwl 4.0.0, with the startup script you can select which version of IntelOwl you want to run (--version). This can be helpful to keep using old versions in case of retrocompatibility issues. The --version parameter checks out the Git Repository to the Tag of the version that you have chosen. This means that if you checkout to a v3.x.x version, you won't have the --version parameter anymore so you would need to manually checkout back to the master branch to use newer versions."},{"location":"IntelOwl/installation/#stop","title":"Stop","text":"

              To stop the application you have to:

              • if executed without -d parameter: press CTRL+C
              • if executed with -d parameter: ./start prod down
              "},{"location":"IntelOwl/installation/#cleanup-of-database-and-application","title":"Cleanup of database and application","text":"

              This is a destructive operation but can be useful to start again the project from scratch

              ./start prod down -- -v

              "},{"location":"IntelOwl/installation/#after-deployment","title":"After Deployment","text":""},{"location":"IntelOwl/installation/#users-creation","title":"Users creation","text":"

              You may want to run docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser after first run to create a superuser. Then you can add other users directly from the Django Admin Interface after having logged with the superuser account. To manage users, organizations and their visibility please refer to this section

              "},{"location":"IntelOwl/installation/#update-and-rebuild","title":"Update and Rebuild","text":""},{"location":"IntelOwl/installation/#rebuilding-the-project-creating-custom-docker-build","title":"Rebuilding the project / Creating custom docker build","text":"

              If you make some code changes and you like to rebuild the project, follow these steps:

              1. ./start test build -- --tag=<your_tag> . to build the new docker image.
              2. Add this new image tag in the docker/test.override.yml file.
              3. Start the containers with ./start test up -- --build.
              "},{"location":"IntelOwl/installation/#update-to-the-most-recent-version","title":"Update to the most recent version","text":"

              To update the project with the most recent available code you have to follow these steps:

              $ cd <your_intel_owl_directory> # go into the project directory\n$ git pull # pull new changes\n$ ./start prod down # kill and destroy the currently running IntelOwl containers\n$ ./start prod up # restart the IntelOwl application\n

              Note

              After an upgrade, sometimes a database error in Celery Containers could happen. That could be related to new DB migrations which are not applied by the main Uwsgi Container yet. Do not worry. Wait few seconds for the Uwsgi container to start correctly, then put down the application again and restart it. The problem should be solved. If not, please feel free to open an issue on Github

              Note

              After having upgraded IntelOwl, in case the application does not start and you get an error like this:
              PermissionError: [Errno 13] Permission denied: '/var/log/intel_owl/django/authentication.log\n
              just run this:
              sudo chown -R www-data:www-data /var/lib/docker/volumes/intel_owl_generic_logs/_data/django\n
              and restart IntelOwl. It should solve the permissions problem.

              Warning

              Major versions of IntelOwl are usually incompatible from one another. Maintainers strive to keep the upgrade between major version easy but it's not always like that. Below you can find the additional process required to upgrade from each major versions."},{"location":"IntelOwl/installation/#updating-to-600-from-a-5xx-version","title":"Updating to >=6.0.0 from a 5.x.x version","text":"

              IntelOwl v6 introduced some major changes regarding how the project is started. Before upgrading, some important things should be checked by the administrator:

              • Docker Compose V1 support has been dropped project-wide. If you are still using a Compose version prior to v2.3.4, please upgrade to a newer version or install Docker Compose V2.
              • IntelOwl is now started with the new Bash start script that has the same options as the old Python start.py script but is more manageable and has decreased the overall project dependencies. The start.py script has now been removed. Please use the new start script instead.
              • The default message broker is now Redis. We have replaced Rabbit-MQ for Redis to allow support for Websockets in the application:
              • This change is transparent if you use our start script to run IntelOwl. That would spawn a Redis instance instead of a Rabbit-MQ one locally.
              • If you were using an external broker like AWS SQS or a managed Rabbit-MQ, they are still supported but we suggest to move to a Redis supported service to simplify the architecture (because Redis is now mandatory for Websockets)
              • Support for multiple jobs with multiple playbooks has been removed. Every Observable or File in the request will be processed by a single playbook.
              • We upgraded the base PostgreSQL image from version 12 to version 16. You have 2 choice:
              • remove your actual database and start from scratch with a new one
              • maintain your database and do not update Postgres. This could break the application at anytime because we do not support it anymore.
              • if you want to keep your old DB, follow the migration procedure you can find below

              Warning

              CARE! We are providing this database migration procedure to help the users to migrate to a new PostgreSQL version. Upgrading PostgreSQL is outside the scope of the IntelOwl project so we do not guarantee that everything will work as intended. In case of doubt, please check the official PostgreSQL documentation. Upgrade at your own risk.

              The database migration procedure is as follows:

              • You have IntelOwl version 5.x.x up and running
              • Bring down the application (you can use the start script or manually concatenate your docker compose configuration )
              • Go inside the docker folder cd docker
              • Bring only the postgres 12 container up docker run -d --name intelowl_postgres_12 -v intel_owl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:12-alpine
              • Dump the entire database. You need the user and the database that you configured during startup for this docker exec -t intelowl_postgres_12 pg_dump -U <POSTGRES_USER> -d <POSTGRES_DB> --no-owner > /tmp/dump_intelowl.sql
              • Stop che container docker container stop intelowl_postgres_12
              • Remove the backup container docker container rm intelowl_postgres_12
              • Remove the postgres volume docker volume rm intel_owl_postgres_data <------------- remove old data, this is not exactly necessary because the new postgres has a different volume name
              • Start the intermediary postgres 16 container docker run -d --name intelowl_postgres_16 -v intelowl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:16-alpine
              • Add the data to the volume cat /tmp/dump_intelowl.sql | docker exec -i intelowl_postgres_16 psql -U <POSTGRES_USER> -d <POSTGRES_DB>
              • Stop the intermediary container docker container stop intelowl_postgres_16
              • Remove the intermediary container docker container rm intelowl_postgres_16
              • Update IntelOwl to the latest version
              • Bring up the application back again (you can use the start script or manually concatenate your docker compose configuration)
              "},{"location":"IntelOwl/installation/#updating-to-500-from-a-4xx-version","title":"Updating to >=5.0.0 from a 4.x.x version","text":"

              IntelOwl v5 introduced some major changes regarding how the plugins and their related configuration are managed in the application. Before upgrading, some important things should be checked by the administrator:

              • A lot of database migrations will need to be applied. Just be patient few minutes once you install the new major release. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page.
              • We moved away from the old big analyzer_config.json which was storing all the base configuration of the Analyzers to a database model (we did the same for all the other plugins types too). This allows us to manage plugins creation/modification/deletion in a more reliable manner and via the Django Admin Interface. If you have created custom plugins and changed those <plugins>_config.json file manually, you would need to re-create those custom plugins again from the Django Admin Interface. To do that please follow the related new documentation
              • We have REMOVED all the analyzers that we deprecated during the v4 releases cycle. Please substitute them with their respective new names, in case they have a replacement.
              • REMOVED Pulsedive_Active_IOC analyzer. Please substitute it with the new Pulsedive analyzer.
              • REMOVED Fortiguard analyzer because endpoint does not work anymore. No substitute.
              • REMOVED Rendertron analyzer not working as intended. No substitute.
              • REMOVED ThreatMiner, SecurityTrails and Robtex various analyzers and substituted with new versions.
              • REMOVED Doc_Info_Experimental. Its functionality (XLM Macro parsing) is moved to Doc_Info
              • REMOVED Strings_Info_Classic. Please use Strings_Info
              • REMOVED Strings_Info_ML. Please use Strings_Info and set the parameter rank_strings to True
              • REMOVED all Yara_Scan_<repo> analyzers. They all went merged in the single Yara analyzer
              • REMOVED Darksearch_Query analyzer because the service does not exist anymore. No substitute.
              • REMOVED UnpacMe_EXE_Unpacker. Please use UnpacMe
              • REMOVED BoxJS_Scan_JavaScript. Please use BoxJS
              • REMOVED all Anomali_Threatstream_<option> analyzers. Now we have a single Anomali_Threatstream analyzer. Use the parameters to select the specific API you need.
              "},{"location":"IntelOwl/installation/#updating-to-500-from-a-3xx-version","title":"Updating to >=5.0.0 from a 3.x.x version","text":"

              This is not supported. Please perform a major upgrade once at a time.

              "},{"location":"IntelOwl/installation/#updating-to-400-from-a-3xx-version","title":"Updating to >=4.0.0 from a 3.x.x version","text":"

              IntelOwl v4 introduced some major changes regarding the permission management, allowing an easier way to manage users and visibility. But that did break the previous available DB. So, to migrate to the new major version you would need to delete your DB. To do that, you would need to delete your volumes and start the application from scratch.

              python3 start.py prod down -v\n

              Please be aware that, while this can be an important effort to manage, the v4 IntelOwl provides an easier way to add, invite and manage users from the application itself. See the Organization section.

              "},{"location":"IntelOwl/installation/#updating-to-200-from-a-1xx-version","title":"Updating to >=2.0.0 from a 1.x.x version","text":"

              Users upgrading from previous versions need to manually move env_file_app, env_file_postgres and env_file_integrations files under the new docker directory.

              "},{"location":"IntelOwl/installation/#updating-to-v13x-from-any-prior-version","title":"Updating to >v1.3.x from any prior version","text":"

              If you are updating to >v1.3.0 from any prior version, you need to execute a helper script so that the old data present in the database doesn't break.

              1. Follow the above updation steps, once the docker containers are up and running execute the following in a new terminal
              docker exec -ti intelowl_uwsgi bash\n

              to get a shell session inside the IntelOwl's container.

              1. Now just copy and paste the below command into this new session,
              curl https://gist.githubusercontent.com/Eshaan7/b111f887fa8b860c762aa38d99ec5482/raw/758517acf87f9b45bd22f06aee57251b1f3b1bbf/update_to_v1.3.0.py | python -\n
              1. If you see \"Update successful!\", everything went fine and now you can enjoy the new features!
              "},{"location":"IntelOwl/introduction/","title":"Introduction","text":"

              IntelOwl Repository

              "},{"location":"IntelOwl/introduction/#introduction","title":"Introduction","text":"

              IntelOwl was designed with the intent to help the community, in particular those researchers that can not afford commercial solutions, in the generation of threat intelligence data, in a simple, scalable and reliable way.

              Main features:

              • Provides enrichment of Threat Intel for malware as well as observables (IP, Domain, URL, hash, etc).
              • This application is built to scale out and to speed up the retrieval of threat info.
              • Thanks to the official libraries pyintelowl and go-intelowl, it can be integrated easily in your stack of security tools to automate common jobs usually performed, for instance, by SOC analysts manually.
              • Intel Owl is composed of:
              • analyzers that can be run to either retrieve data from external sources (like VirusTotal or AbuseIPDB) or to generate intel from internally available tools (like Yara or Oletools)
              • connectors that can be run to export data to external platforms (like MISP or OpenCTI)
              • visualizers that can be run to create custom visualizations of analyzers results
              • playbooks that are meant to make analysis easily repeatable
              • API REST written in Django and Python 3.9.
              • Built-in frontend client written in ReactJS, with certego-ui: provides features such as dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, etc.
              "},{"location":"IntelOwl/introduction/#publications-and-media","title":"Publications and media","text":"

              To know more about the project and its growth over time, you may be interested in reading the following official blog posts and/or videos:

              • HelpNetSecurity interview
              • FIRSTCON 24 Fukuoka (Japan)
              • The Honeynet Workshop: Denmark 2024
              • Certego Blog: v6 Announcement (in Italian)
              • HackinBo 2023 Presentation (in Italian)
              • Certego Blog: v.5.0.0 Announcement
              • Youtube demo: IntelOwl v4
              • Certego Blog: v.4.0.0 Announcement
              • Honeynet Blog: v3.0.0 Announcement
              • Intel Owl on Daily Swig
              • Honeynet Blog: v1.0.0 Announcement
              • Certego Blog: First announcement

              Feel free to ask everything it comes to your mind about the project to the author: Matteo Lodi (Twitter).

              We also have a dedicated twitter account for the project: @intel_owl.

              "},{"location":"IntelOwl/usage/","title":"Usage","text":"

              This page includes the most important things to know and understand when using IntelOwl.

              "},{"location":"IntelOwl/usage/#how-to-interact-with-intelowl","title":"How to interact with IntelOwl","text":"

              Intel Owl main objective is to provide a single API interface to query in order to retrieve threat intelligence at scale.

              There are multiple ways to interact with the Intel Owl APIs,

              1. Web Interface

              2. Built-in Web interface with dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, tags management and more features

              3. pyIntelOwl (CLI/SDK)

              4. Official Python client that is available at: PyIntelOwl,

              5. Can be used as a library for your own python projects or...
              6. directly via the command line interface.

              7. goIntelOwl (CLI/SDK)

              8. Official GO client that is available at: go-intelowl

              Hint: Tokens Creation

              The server authentication is managed by API tokens. So, if you want to interact with Intel Owl, you have two ways to do that:
              • If you are a normal user, you can go to the \"API Access/Sessions\" section of the GUI and create a Token there.
              • If you are an administrator of IntelOwl, you can create one or more unprivileged users from the Django Admin Interface and then generate a token for those users.
              Afterwards you can leverage the created tokens with the Intel Owl Client."},{"location":"IntelOwl/usage/#plugins-framework","title":"Plugins Framework","text":"

              Plugins are the core modular components of IntelOwl that can be easily added, changed and customized. There are several types of plugins:

              • Analyzers
              • Connectors
              • Pivots
              • Visualizers
              • Ingestors
              • Playbooks
              "},{"location":"IntelOwl/usage/#analyzers","title":"Analyzers","text":"

              Analyzers are the most important plugins in IntelOwl. They allow to perform data extraction on the observables and/or files that you would like to analyze.

              "},{"location":"IntelOwl/usage/#analyzers-list","title":"Analyzers list","text":"

              The following is the list of the available analyzers you can run out-of-the-box. You can also navigate the same list via the

              • Graphical Interface: once your application is up and running, go to the \"Plugins\" section
              • pyintelowl: $ pyintelowl get-analyzer-config
              "},{"location":"IntelOwl/usage/#file-analyzers","title":"File analyzers:","text":""},{"location":"IntelOwl/usage/#internal-tools","title":"Internal tools","text":"
              • APKiD: APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.
              • BoxJS_Scan_Javascript: Box-JS is a tool for studying JavaScript malware.
              • Capa_Info: Capa detects capabilities in executable files
              • Capa_Info_Shellcode: Capa detects capabilities in shellcode
              • ClamAV: scan a file via the ClamAV AntiVirus Engine. IntelOwl automatically keep ClamAV updated with official and unofficial open source signatures
              • Doc_Info: static document analysis with new features to analyze XLM macros, encrypted macros and more (combination of Oletools and XLMMacroDeobfuscator)
              • ELF_Info: static ELF analysis with pyelftools and telfhash
              • File_Info: static generic File analysis (hashes, magic and exiftool)
              • Floss: Mandiant Floss Obfuscated String Solver in files
              • Hfinger: create fingerprints of malware HTTPS requests using Hfinger
              • PE_Info: static PE analysis with pefile
              • PEframe_Scan: Perform static analysis on Portable Executable malware and malicious MS Office documents with PeFrame
              • Permhash: create hash of manifest permssions found in APK, Android manifest, Chrome extensions or Chrome extension manifest using Permhash
              • PDF_Info: static PDF analysis (peepdf + pdfid)
              • Qiling_Linux: Qiling qiling linux binary emulation.
              • Qiling_Linux_Shellcode: Qiling qiling linux shellcode emulation.
              • Qiling_Windows: Qiling qiling windows binary emulation.
              • Qiling_Windows_Shellcode: Qiling qiling windows shellcode emulation.
              • Quark_Engine: Quark Engine is an Obfuscation-Neglect Android Malware Scoring System.
              • Rtf_Info: static RTF analysis (Oletools)
              • Signature_Info: PE signature extractor with osslsigncode
              • Speakeasy: Mandiant Speakeasy binary emulation
              • SpeakEasy_Shellcode: Mandiant Speakeasy shellcode emulation
              • Strings_Info: Strings extraction. Leverages Mandiant's Stringsifter
              • Suricata: Analyze PCAPs with open IDS signatures with Suricata engine
              • Thug_HTML_Info: Perform hybrid dynamic/static analysis on a HTML file using Thug low-interaction honeyclient
              • Xlm_Macro_Deobfuscator: XlmMacroDeobfuscator deobfuscate xlm macros
              • Yara: scan a file with
              • ATM malware yara rules
              • bartblaze yara rules
              • community yara rules
              • StrangerealIntel
              • Neo23x0 yara rules
              • Intezer yara rules
              • Inquest yara rules
              • McAfee yara rules
              • Samir Threat Hunting yara rules
              • Stratosphere yara rules
              • Mandiant yara rules
              • ReversingLabs yara rules
              • YARAify rules
              • SIFalcon rules
              • Elastic rules
              • JPCERTCC Yara rules
              • HuntressLab Yara rules
              • elceef Yara Rules
              • dr4k0nia Yara rules
              • Facebook Yara rules
              • edelucia Yara rules
              • LOLDrivers Yara Rules
              • your own added signatures. See Advanced-Usage for more details.
              "},{"location":"IntelOwl/usage/#external-services","title":"External services","text":"
              • CapeSandbox: CAPESandbox automatically scans suspicious files using the CapeSandbox API. Analyzer works for private instances as well.
              • Cymru_Hash_Registry_Get_File: Check if a particular file is known to be malware by Team Cymru
              • Cuckoo_Scan: scan a file on Cuckoo (this analyzer is disabled by default. You have to change that flag in the config to use it)
              • DocGuard_Upload_File: Analyze office files in seconds. DocGuard.
              • Dragonfly_Emulation: Emulate malware against Dragonfly sandbox by Certego S.R.L.
              • FileScan_Upload_File: Upload your file to extract IoCs from executable files, documents and scripts via FileScan.io API.
              • HashLookupServer_Get_File: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
              • HybridAnalysis_Get_File: check file hash on HybridAnalysis sandbox reports
              • Intezer_Scan: scan a file on Intezer. Register for a free community account here. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
              • Malpedia_Scan: scan a binary or a zip file (pwd:infected) against all the yara rules available in Malpedia
              • MalwareBazaar_Get_File: Check if a particular malware sample is known to MalwareBazaar
              • MISPFIRST_Check_Hash: check a file hash on the FIRST MISP instance
              • MISP_Check_Hash: check a file hash on a MISP instance
              • MWDB_Scan: mwdblib Retrieve malware file analysis from repository maintained by CERT Polska MWDB. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
              • OTX_Check_Hash: check file hash on Alienvault OTX
              • SublimeSecurity: Analyze an Email with Sublime Security live flow
              • Triage_Scan: leverage Triage sandbox environment to scan various files
              • UnpacMe: UnpacMe is an automated malware unpacking service
              • Virushee_Scan: Check file hash on Virushee API. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
              • VirusTotal_v3_File: check the file hash on VirusTotal. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
              • YARAify_File_Scan: scan a file against public and non-public YARA and ClamAV signatures in YARAify public service
              • YARAify_File_Search: scan an hash against YARAify database
              • Zippy_scan : Zippy: Fast method to classify text as AI or human-generated; takes in lzma,zlib,brotli as input based engines; ensemble being default.
              • Blint: Blint is a Binary Linter that checks the security properties and capabilities of your executables. Supported binary formats: - Android (apk, aab) - ELF (GNU, musl) - PE (exe, dll) - Mach-O (x64, arm64)
              "},{"location":"IntelOwl/usage/#observable-analyzers-ip-domain-url-hash","title":"Observable analyzers (ip, domain, url, hash)","text":""},{"location":"IntelOwl/usage/#internal-tools_1","title":"Internal tools","text":"
              • CheckDMARC: An SPF and DMARC DNS records validator for domains.
              • DNStwist: Scan a url/domain to find potentially malicious permutations via dns fuzzing. dnstwist repo
              • Thug_URL_Info: Perform hybrid dynamic/static analysis on a URL using Thug low-interaction honeyclient
              "},{"location":"IntelOwl/usage/#external-services_1","title":"External services","text":"
              • AbuseIPDB: check if an ip was reported on AbuseIPDB
              • Abusix: get abuse contacts of an IP address from Abusix
              • BGP Ranking: BGP-Ranking provides a way to collect such malicious activities, aggregate the information per ASN and provide a ranking model to rank the ASN from the most malicious to the less malicious ASN.
              • Anomali_Threatstream_PassiveDNS: Return information from passive dns of Anomali. On Anomali Threatstream PassiveDNS Api.
              • Auth0: scan an IP against the Auth0 API
              • BinaryEdge: Details about an Host. List of recent events for the specified host, including details of exposed ports and services using IP query and return list of subdomains known from the target domains using domain query
              • BitcoinAbuse : Check a BTC address against bitcoinabuse.com, a public database of BTC addresses used by hackers and criminals.
              • Censys_Search: scan an IP address against Censys View API
              • CheckPhish: CheckPhish can detect phishing and fraudulent sites.
              • CIRCLPassiveDNS: scan an observable against the CIRCL Passive DNS DB
              • CIRCLPassiveSSL: scan an observable against the CIRCL Passive SSL DB
              • Classic_DNS: Retrieve current domain resolution with default DNS
              • CloudFlare_DNS: Retrieve current domain resolution with CloudFlare DoH (DNS over HTTPS)
              • CloudFlare_Malicious_Detector: Leverages CloudFlare DoH to check if a domain is related to malware
              • Crowdsec: check if an IP was reported on Crowdsec Smoke Dataset
              • Cymru_Hash_Registry_Get_Observable: Check if a particular hash is available in the malware hash registry of Team Cymru
              • DNSDB: scan an observable against the Passive DNS Farsight Database (support both v1 and v2 versions)
              • DNS0_EU: Retrieve current domain resolution with DNS0.eu DoH (DNS over HTTPS)
              • DNS0_EU_Malicious_Detector: Check if a domain or an url is marked as malicious in DNS0.eu database (Zero service)
              • DocGuard_Get: check if an hash was analyzed on DocGuard. DocGuard
              • Feodo_Tracker: Feodo Tracker offers various blocklists, helping network owners to protect their users from Dridex and Emotet/Heodo.
              • FileScan_Search: Finds reports and uploaded files by various tokens, like hash, filename, verdict, IOCs etc via FileScan.io API.
              • FireHol_IPList: check if an IP is in FireHol's IPList
              • GoogleSafebrowsing: Scan an observable against GoogleSafeBrowsing DB
              • GoogleWebRisk: Scan an observable against WebRisk API (Commercial version of Google Safe Browsing). Check the docs to enable this properly
              • Google_DNS: Retrieve current domain resolution with Google DoH (DNS over HTTPS)
              • GreedyBear: scan an IP or a domain against the GreedyBear API (requires API key)
              • GreyNoise: scan an IP against the Greynoise API (requires API key)
              • GreyNoiseCommunity: scan an IP against the Community Greynoise API (requires API key))
              • Greynoise_Labs: scan an IP against the Greynoise API (requires authentication token which can be obtained from cookies on Greynoise website after launching the playground from here)
              • HashLookupServer_Get_Observable: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
              • HoneyDB_Get: HoneyDB IP lookup service
              • HoneyDB_Scan_Twitter: scan an IP against HoneyDB.io's Twitter Threat Feed
              • Hunter_How: Scans IP and domain against Hunter_How API.
              • Hunter_Io: Scans a domain name and returns set of data about the organisation, the email address found and additional information about the people owning those email addresses.
              • HybridAnalysis_Get_Observable: search an observable in the HybridAnalysis sandbox reports
              • IP2WHOIS: API Docs IP2Location.io IP2WHOIS Domain WHOIS API helps users to obtain domain information and WHOIS record by using a domain name.
              • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
              • InQuest_DFI: Deep File Inspection by InQuest Labs
              • InQuest_IOCdb: Indicators of Compromise Database by InQuest Labs
              • InQuest_REPdb: Search in InQuest Lab's Reputation Database
              • IPApi: Get information about IPs using batch-endpoint and DNS using DNS-endpoint.
              • IPInfo: Location Information about an IP
              • Ip2location: API Docs IP2Location.io allows users to check IP address location in real time. (Supports both with or without key)
              • Intezer_Get: check if an analysis related to a hash is available in Intezer. Register for a free community account here.
              • Koodous: koodous API get information about android malware.
              • MalwareBazaar_Get_Observable: Check if a particular malware hash is known to MalwareBazaar
              • MalwareBazaar_Google_Observable: Check if a particular IP, domain or url is known to MalwareBazaar using google search
              • MaxMindGeoIP: extract GeoIP info for an observable
              • MISP: scan an observable on a MISP instance
              • MISPFIRST: scan an observable on the FIRST MISP instance
              • Mmdb_server: Mmdb_server mmdb-server is an open source fast API server to lookup IP addresses for their geographic location, AS number.
              • Mnemonic_PassiveDNS : Look up a domain or IP using the Mnemonic PassiveDNS public API.
              • MWDB_Get: mwdblib Retrieve malware file analysis by hash from repository maintained by CERT Polska MWDB.
              • Netlas: search an IP against Netlas
              • ONYPHE: search an observable in ONYPHE
              • OpenCTI: scan an observable on an OpenCTI instance
              • OTXQuery: scan an observable on Alienvault OTX
              • Phishstats: Search PhishStats API to determine if an IP/URL/domain is malicious.
              • Phishtank: Search an url against Phishtank API
              • PhishingArmy: Search an observable in the PhishingArmy blocklist
              • Pulsedive: Scan indicators and retrieve results from Pulsedive's API.
              • Quad9_DNS: Retrieve current domain resolution with Quad9 DoH (DNS over HTTPS)
              • Quad9_Malicious_Detector: Leverages Quad9 DoH to check if a domain is related to malware
              • Robtex: scan a domain/IP against the Robtex Passive DNS DB
              • Securitytrails: scan an IP/Domain against Securitytrails API
              • Shodan_Honeyscore: scan an IP against Shodan Honeyscore API
              • Shodan_Search: scan an IP against Shodan Search API
              • Spyse: Scan domains, IPs, emails and CVEs using Spyse's API. Register here.
              • SSAPINet: get a screenshot of a web page using screenshotapi.net (external source); additional config options can be added to extra_api_params in the config.
              • Stalkphish: Search Stalkphish API to retrieve information about a potential phishing site (IP/URL/domain/Generic).
              • Stratosphere_Blacklist: Cross-reference an IP from blacklists maintained by Stratosphere Labs
              • TalosReputation: check an IP reputation from Talos
              • ThreatFox: search for an IOC in ThreatFox's database
              • Threatminer: retrieve data from Threatminer API
              • TorNodesDanMeUk: check if an IP is a Tor Node using a list of all Tor nodes provided by dan.me.uk
              • TorProject: check if an IP is a Tor Exit Node
              • Triage_Search: Search for reports of observables or upload from URL on triage cloud
              • Tranco: Check if a domain is in the latest Tranco ranking top sites list
              • URLhaus: Query a domain or URL against URLhaus API.
              • UrlScan_Search: Search an IP/domain/url/hash against URLScan API
              • UrlScan_Submit_Result: Submit & retrieve result of an URL against URLScan API
              • Virushee_CheckHash: Search for a previous analysis of a file by its hash (SHA256/SHA1/MD5) on Virushee API.
              • VirusTotal_v3_Get_Observable: search an observable in the VirusTotal DB
              • Whoisxmlapi: Fetch WHOIS record data, of a domain name, an IP address, or an email address.
              • WhoIs_RipeDB_Search : Fetch whois record data of an IP address from Ripe DB using their search API (no API key required)
              • XForceExchange: scan an observable on IBM X-Force Exchange
              • YARAify_Search: lookup a file hash in Abuse.ch YARAify
              • YETI (Your Everyday Threat Intelligence): scan an observable on a YETI instance.
              • Zoomeye: Zoomeye Cyberspace Search Engine recording information of devices, websites, services and components etc..
              • Validin:Validin investigates historic and current data describing the structure and composition of the internet.
              • TweetFeed: TweetFeed collects Indicators of Compromise (IOCs) shared by the infosec community at Twitter.\\r\\nHere you will find malicious URLs, domains, IPs, and SHA256/MD5 hashes.
              • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
              • CyCat: CyCat or the CYbersecurity Resource CATalogue aims at mapping and documenting, in a single formalism and catalogue available cybersecurity tools, rules, playbooks, processes and controls.
              • Vulners: Vulners is the most complete and the only fully correlated security intelligence database, which goes through constant updates and links 200+ data sources in a unified machine-readable format. It contains 8 mln+ entries, including CVEs, advisories, exploits, and IoCs \u2014 everything you need to stay abreast on the latest security threats.
              • AILTypoSquatting:AILTypoSquatting is a Python library to generate list of potential typo squatting domains with domain name permutation engine to feed AIL and other systems.
              "},{"location":"IntelOwl/usage/#generic-analyzers-email-phone-number-etc-anything-really","title":"Generic analyzers (email, phone number, etc.; anything really)","text":"

              Some analyzers require details other than just IP, URL, Domain, etc. We classified them as generic Analyzers. Since the type of field is not known, there is a format for strings to be followed.

              "},{"location":"IntelOwl/usage/#internal-tools_2","title":"Internal tools","text":"
              • CyberChef: Run a query on a CyberChef server using pre-defined or custom recipes.
              "},{"location":"IntelOwl/usage/#external-services_2","title":"External services","text":"
              • Anomali_Threatstream_Confidence: Give max, average and minimum confidence of maliciousness for an observable. On Anomali Threatstream Confidence API.
              • Anomali_Threatstream_Intelligence: Search for threat intelligence information about an observable. On Anomali Threatstream Intelligence API.
              • CRXcavator: scans a chrome extension against crxcavator.io
              • Dehashed_Search: Query any observable/keyword against https://dehashed.com's search API.
              • EmailRep: search an email address on emailrep.io
              • HaveIBeenPwned: HaveIBeenPwned checks if an email address has been involved in a data breach
              • IntelX_Intelligent_Search: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
              • IntelX_Phonebook: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
              • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
              • MISP: scan an observable on a MISP instance
              • VirusTotal_v3_Intelligence_Search: Perform advanced queries with VirusTotal Intelligence (requires paid plan)
              • WiGLE: Maps and database of 802.11 wireless networks, with statistics, submitted by wardrivers, netstumblers, and net huggers.
              • YARAify_Generics: lookup a YARA rule (default), ClamAV rule, imphash, TLSH, telfhash or icon_dash in YARAify
              • PhoneInfoga : PhoneInfoga is one of the most advanced tools to scan international phone numbers.
              • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
              "},{"location":"IntelOwl/usage/#optional-analyzers","title":"Optional analyzers","text":"

              Some analyzers are optional and need to be enabled explicitly.

              "},{"location":"IntelOwl/usage/#connectors","title":"Connectors","text":"

              Connectors are designed to run after every successful analysis which makes them suitable for automated threat-sharing. They support integration with other SIEM/SOAR projects, specifically aimed at Threat Sharing Platforms.

              "},{"location":"IntelOwl/usage/#connectors-list","title":"Connectors list","text":"

              The following is the list of the available connectors. You can also navigate the same list via the

              • Graphical Interface: once your application is up and running, go to the \"Plugins\" section
              • pyintelowl: $ pyintelowl get-connector-config
              "},{"location":"IntelOwl/usage/#list-of-pre-built-connectors","title":"List of pre-built Connectors","text":"
              • MISP: automatically creates an event on your MISP instance, linking the successful analysis on IntelOwl.
              • OpenCTI: automatically creates an observable and a linked report on your OpenCTI instance, linking the the successful analysis on IntelOwl.
              • YETI: YETI = Your Everyday Threat Intelligence. find or create observable on YETI, linking the successful analysis on IntelOwl.
              • Slack: Send the analysis link to a Slack channel (useful for external notifications)
              • EmailSender: Send a generic email.
              • AbuseSubmitter: Send an email to request to take down a malicious domain.
              "},{"location":"IntelOwl/usage/#pivots","title":"Pivots","text":"

              With IntelOwl v5.2.0 we introduced the Pivot Plugin.

              Pivots are designed to create a job from another job. This plugin allows the user to set certain conditions that trigger the execution of one or more subsequent jobs, strictly connected to the first one.

              This is a \"SOAR\" feature that allows the users to connect multiple analysis together.

              "},{"location":"IntelOwl/usage/#list-of-pre-built-pivots","title":"List of pre-built Pivots","text":"
              • TakedownRequestToAbuseIp: This Plugin leverages results from DNS resolver analyzers to extract a valid IP address to pivot to the Abusix analyzer.
              • AbuseIpToSubmission: This Plugin leverages results from the Abusix analyzer to extract the abuse contacts of an IP address to pivot to the AbuseSubmitter connector.

              You can build your own custom Pivot with your custom logic with just few lines of code. See the Contribute section for more info.

              "},{"location":"IntelOwl/usage/#creating-pivots-from-the-gui","title":"Creating Pivots from the GUI","text":"

              From the GUI, the users can pivot in two ways:

              • If a Job executed a Visualizer, it is possible to select a field extracted and analyze its value by clicking the \"Pivot\" button (see following image). In this way, the user is able to \"jump\" from one indicator to another.

              • Starting from an already existing Investigation, it is possible to select a Job block and click the \"Pivot\" button to analyze the same observable again, usually choosing another Playbook (see following image)

              In both cases, the user is redirected to the Scan Page that is precompiled with the observable selected. Then the user would be able to select the Playbook to execute in the new job.

              After the new Job is started, a new Investigation will be created (if it does not already exist) and both the jobs will be added to the same Investigation.

              In the following image you can find an example of an Investigation composed by 3 pivots generated manually:

              • leveraging the first way to create a Pivot, the 2 Jobs that analyzed IP addresses have been generated from the first test\\.com Job
              • leveraging the second way to create a Pivot, the second test\\.com analysis had been created with a different Playbook.

              "},{"location":"IntelOwl/usage/#visualizers","title":"Visualizers","text":"

              With IntelOwl v5 we introduced a new plugin type called Visualizers. You can leverage it as a framework to create custom aggregated and simplified visualization of analyzer results.

              Visualizers are designed to run after the analyzers and the connectors. The visualizer adds logic after the computations, allowing to show the final result in a different way than merely the list of reports.

              Visualizers can be executed only during Scans through the playbook that has been configured on the visualizer itself.

              This framework is extremely powerful and allows every user to customize the GUI as they wish. But you know...with great power comes great responsability. To fully leverage this framework, you would need to put some effort in place. You would need to understand which data is useful for you and then write few code lines that would create your own GUI. To simplify the process, take example from the pre-built visualizers listed below and follow the dedicated documentation.

              "},{"location":"IntelOwl/usage/#list-of-pre-built-visualizers","title":"List of pre-built Visualizers","text":"
              • DNS: displays the aggregation of every DNS analyzer report
              • Yara: displays the aggregation of every matched rule by the Yara Analyzer
              • Domain_Reputation: Visualizer for the Playbook \"Popular_URL_Reputation_Services\"
              • IP_Reputation: Visualizer for the Playbook \"Popular_IP_Reputation_Services\"
              • Pivot: Visualizer that can be used in a Playbook to show the Pivot execution result. See Pivots for more info.
              "},{"location":"IntelOwl/usage/#ingestors","title":"Ingestors","text":"

              With IntelOwl v5.1.0 we introduced the Ingestor Plugin.

              Ingestors allow to automatically insert IOC streams from outside sources to IntelOwl itself. Each Ingestor must have a Playbook attached: this will allow to create a Job from every IOC retrieved.

              Ingestors are system-wide and disabled by default, meaning that only the administrator are able to configure them and enable them. Ingestors can be spammy so be careful about enabling them.

              A very powerful use is case is to combine Ingestors with Connectors to automatically extract data from external sources, analyze them with IntelOwl and push them externally to another platform (like MISP or a SIEM)

              "},{"location":"IntelOwl/usage/#list-of-pre-built-ingestors","title":"List of pre-built Ingestors","text":"
              • ThreatFox: Retrieves daily ioc from https://threatfox.abuse.ch/ and analyze them.
              "},{"location":"IntelOwl/usage/#playbooks","title":"Playbooks","text":"

              Playbooks are designed to be easy to share sequence of running Plugins (Analyzers, Connectors, ...) on a particular kind of observable.

              If you want to avoid to re-select/re-configure a particular combination of analyzers and connectors together every time, you should create a playbook out of it and use it instead. This is time saver.

              This is a feature introduced since IntelOwl v4.1.0! Please provide feedback about it!

              "},{"location":"IntelOwl/usage/#playbooks-list","title":"Playbooks List","text":"

              The following is the list of the available pre-built playbooks. You can also navigate the same list via the

              • Graphical Interface: once your application is up and running, go to the \"Plugins\" section
              • pyintelowl: $ pyintelowl get-playbook-config
              "},{"location":"IntelOwl/usage/#list-of-pre-built-playbooks","title":"List of pre-built playbooks","text":"
              • FREE_TO_USE_ANALYZERS: A playbook containing all free to use analyzers.
              • Sample_Static_Analysis: A playbook containing all analyzers that perform static analysis on files.
              • Popular_URL_Reputation_Services: Collection of the most popular and free reputation analyzers for URLs and Domains
              • Popular_IP_Reputation_Services: Collection of the most popular and free reputation analyzers for IP addresses
              • Dns: A playbook containing all dns providers
              • Takedown_Request: Start investigation to request to take down a malicious domain. A mail will be sent to the domain's abuse contacts found
              • Abuse_IP: Playbook containing the Abusix analyzer. It is executed after the Takedown_Request playbook
              • Send_Abuse_Email: Playbook containing the AbuseSubmitter connector to send an email to request to take down a malicious domain. It is executed after the Abuse_IP playbook
              "},{"location":"IntelOwl/usage/#playbooks-creation-and-customization","title":"Playbooks creation and customization","text":"

              You can create new playbooks in different ways, based on the users you want to share them with:

              If you want to share them to every user in IntelOwl, create them via the Django Admin interface at /admin/playbooks_manager/playbookconfig/.

              If you want share them to yourself or your organization only, you need to leverage the \"Save as Playbook\" button that you can find on the top right of the Job Result Page. In this way, after you have done an analysis, you can save the configuration of the Plugins you executed for re-use with a single click.

              The created Playbook would be available to yourself only. If you want either to share it with your organization or to delete it, you need to go to the \"Plugins\" section and enable it manually by clicking the dedicated button.

              "},{"location":"IntelOwl/usage/#generic-plugin-creation-configuration-and-customization","title":"Generic Plugin Creation, Configuration and Customization","text":"

              If you want to create completely new Plugins (not based on already existing python modules), please refer to the Contribute section. This is usually the case when you want to integrate IntelOwl with either a new tool or a new service.

              On the contrary, if you would like to just customize the already existing plugins, this is the place.

              "},{"location":"IntelOwl/usage/#superuser-customization","title":"SuperUser customization","text":"

              If you are an IntelOwl superuser, you can create, modify, delete analyzers based on already existing modules by changing the configuration values inside the Django Admin interface at:

              • for analyzers: /admin/analyzers_manager/analyzerconfig/.
              • for connectors: /admin/connectors_manager/connectorconfig/.
              • ...and so on for all the Plugin types.

              The following are the most important fields that you can change without touching the source code:

              • Name: Name of the analyzer
              • Description: Description of the analyzer
              • Disabled: you can choose to disable certain analyzers, then they won't appear in the dropdown list and won't run if requested.
              • Python Module: Python path of the class that will be executed. This should not be changed most of the times.
              • Maximum TLP: see TLP Support
              • Soft Time Limit: this is the maximum time (in seconds) of execution for an analyzer. Once reached, the task will be killed (or managed in the code by a custom Exception). Default 300.
              • Routing Key: this takes effects only when multi-queue is enabled. Choose which celery worker would execute the task: local (ideal for tasks that leverage local applications like Yara), long (ideal for long tasks) or default (ideal for simple webAPI-based analyzers).

              For analyzers only:

              • Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a different mimetype from the ones you specified, it won't be executed
              • Not Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a mimetype from the ones you specified, it won't be executed
              • Observable Supported: can be populated as a list. If set, if you ask to analyze an observable that is not in this list, it won't be executed. Valid values are: ip, domain, url, hash, generic.

              For connectors only:

              • Run on Failure (default: true): if they can be run even if the job has status reported_with_fails

              For visualizers only:

              • Playbooks: list of playbooks that trigger the specified visualizer execution.

              Sometimes, it may happen that you would like to create a new analyzer very similar to an already existing one. Maybe you would like to just change the description and the default parameters. A helpful way to do that without having to copy/pasting the entire configuration, is to click on the analyzer that you want to copy, make the desired changes, and click the save as new button.

              Warning

              Changing other keys can break a plugin. In that case, you should think about duplicating the configuration entry or python module with your changes.

              Other options can be added at the \"Python module\" level and not at the Plugin level. To do that, go to: admin/api_app/pythonmodule/ and select the Python module used by the Plugin that you want to change. For example, the analyzer AbuseIPDB uses the Python module abuseipdb.AbuseIPDB.

              Once there, you'll get this screen:

              There you can change the following values:

              • Update Schedule: if the analyzer require some sort of update (local database, local rules, ...), you can specify the crontab schedule to update them.
              • Health Check Schedule: if the analyzer has implemented a Health Check, you can specify the crontab schedule to check whether the service works or not.
              "},{"location":"IntelOwl/usage/#parameters","title":"Parameters","text":"

              Each Plugin could have one or more parameters available to be configured. These parameters allow the users to customize the Plugin behavior.

              There are 2 types of Parameters:

              • classic Parameters
              • Secrets: these parameters usually manage sensitive data, like API keys.

              To see the list of these parameters:

              • You can view the \"Plugin\" Section in IntelOwl to have a complete and updated view of all the options available
              • You can view the parameters by exploring the Django Admin Interface:
              • admin/api_app/parameter/
              • or at the very end of each Plugin configuration like /admin/analyzers_manager/analyzerconfig/

              You can change the Plugin Parameters at 5 different levels:

              • if you are an IntelOwl superuser, you can go in the Django Admin Interface and change the default values of the parameters for every plugin you like. This option would change the default behavior for every user in the platform.
              • if you are either Owner or Admin of an org, you can customize the default values of the parameters for every member of the organization by leveraging the GUI in the \"Organization Config\" section. This overrides the previous option.
              • if you are a normal user, you can customize the default values of the parameters for your analysis only by leveraging the GUI in the \"Plugin config\" section. This overrides the previous option.
              • You can choose to provide runtime configuration when requesting an analysis that will override the previous options. This override is done only for the specific analysis. See Customize analyzer execution at time of request

              Playbook Exception

              Please remember that, if you are executing a Playbook, the \"Runtime configuration\" of the Playbook take precedence over the Plugin Configuration.

              Plugin Configuration Order

              Due to the multiple chances that are given to customize the parameters of the Plugins that are executed, it may be easy to confuse the order and launch Plugins without the awereness of what you are doing. This is the order to define which values are used for the parameters, starting by the most important element: - Runtime Configuration at Time of Request. - Runtime Configuration of the Playbook (if a Playbook is used and the Runtime Configuration at Time of Request is empty) - Plugin Configuration of the User - Plugin Configuration of the Organization - Default Plugin Configuration of the Parameter If you are using the GUI, please remember that you can always check the Parameters before starting a \"Scan\" by clicking at the \"Runtime configuration\" ![img.png](./static/runtime_config.png) button. Example: ![img.png](./static/runtime_config_2.png)"},{"location":"IntelOwl/usage/#enabling-or-disabling-plugins","title":"Enabling or Disabling Plugins","text":"

              By default, each available plugin is configured as either disabled or not. The majority of them are enabled by default, while others may be disabled to avoid potential problems in the application usability for first time users.

              Considering the impact that this change could have in the application, the GUI does not allow a normal user to enable/disable any plugin. On the contrary, users with specific privileges may change this configuration:

              • Org Administrators may leverage the feature documented here to enable/disable plugins for their org. This can be helpful to control users' behavior.
              • IntelOwl Superusers (full admin) can go to the Django Admin Interface and enable/disable them from there. This operation does overwrite the Org administrators configuration. To find the plugin to change, they'll need to first choose the section of its type (\"ANALYZERS_MANAGER\", \"CONNECTORS_MANAGER\", etc), then select the chosen plugin, change the flag on that option and save the plugin by pressing the right button.

              "},{"location":"IntelOwl/usage/#special-plugins-operations","title":"Special Plugins operations","text":"

              All plugins, i.e. analyzers and connectors, have kill and retry actions. In addition to that, all docker-based analyzers and connectors have a healthcheck action to check if their associated instances are up or not.

              • kill:

              Stop a plugin whose status is running/pending:

              • GUI: Buttons on reports table on job result page.
              • PyIntelOwl: IntelOwl.kill_analyzer and IntelOwl.kill_connector function.
              • CLI: $ pyintelowl jobs kill-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs kill-connector <job_id> <connector_name>
              • API: PATCH /api/job/{job_id}/{plugin_type/{plugin_name}/kill and PATCH /api/job/{job_id}/connector/{connector_name}/kill

              • retry:

              Retry a plugin whose status is failed/killed:

              • GUI: Buttons on reports table on job result page.
              • PyIntelOwl: IntelOwl.retry_analyzer and IntelOwl.retry_connector function,
              • CLI: $ pyintelowl jobs retry-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs retry-connector <job_id> <connector_name>
              • API: PATCH /api/job/{job_id}/{plugin_type}/{plugin_name}/retry

              • healthcheck:

              Check if a plugin is able to connect to its provider:

              • GUI: Buttons on every plugin table.
              • PyIntelOwl: IntelOwl.analyzer_healthcheck and IntelOwl.connector_healthcheck methods.
              • CLI: $ pyintelowl analyzer-healthcheck <analyzer_name> and $ pyintelowl connector-healthcheck <connector_name>
              • API: GET /api/{plugin_type}/{plugin_name}/healthcheck

              • pull:

              Update a plugin with the newest rules/database:

              • GUI: Buttons on every plugin table.
              • API: POST /api/{plugin_type}/{plugin_name}/pull
              "},{"location":"IntelOwl/usage/#tlp-support","title":"TLP Support","text":"

              The Traffic Light Protocol (TLP) is a standard that was created to facilitate greater sharing of potentially sensitive information and more effective collaboration.

              IntelOwl is not a threat intel sharing platform, like the MISP platform. However, IntelOwl is able to share analysis results to external platforms (via Connectors) and to send possible privacy related information to external services (via Analyzers).

              This is why IntelOwl does support a customized version of the Traffic Light Protocol (TLP): to allow the user to have a better knowledge of how their data are being shared.

              Every Analyzer and Connector can be configured with a maximum_tlp value. Based on that value, IntelOwl understands if the specific plugin is allowed or not to run (e.g. if maximum_tlp is GREEN, it would run for analysis with TLPs WHITE and GREEN only)

              These is how every available TLP value behaves once selected for an analysis execution:

              1. CLEAR: no restriction (WHITE was replaced by CLEAR in TLP v2.0, but WHITE is supported for retrocompatibility)
              2. GREEN: disable analyzers that could impact privacy
              3. AMBER (default): disable analyzers that could impact privacy and limit view permissions to my group
              4. RED: disable analyzers that could impact privacy, limit view permissions to my group and do not use any external service
              "},{"location":"IntelOwl/usage/#running-a-plugin","title":"Running a plugin","text":"

              A plugin can be run when all of the following requirements have been satisfied:

              1. All the required parameters of the plugin have been configured
              2. The plugin is not disabled
              3. The plugin is not disabled for the user's organization
              4. If the plugin has a health check schedule, the last check has to be successful
              5. The TLP selected to run the plugin cannot be higher than the maximum TLP configured for that plugin
              6. The observable classification or the file mimetype has to be supported by the plugin
              "},{"location":"IntelOwl/usage/#investigations-framework","title":"Investigations Framework","text":"

              Investigations are a new framework introduced in IntelOwl v6 with the goal to allow the users to connect the analysis they do with each other.

              In this way the analysts can use IntelOwl as the starting point of their \"Investigations\", register their findings, correlate the information found, and collaborate...all in a single place.

              Things to know about the framework:

              • an Investigation is a superset of IntelOwl Jobs. It can have attached one or more existing IntelOwl Jobs
              • an Investigation contains a \"description\" section that can be changed and updated at anytime with new information from the analysts.
              • modification to the Investigation (description, jobs, etc) can be done by every member of the Organization where the creator of the Investigation belongs. However only they creator can delete an Investigation.
              "},{"location":"IntelOwl/usage/#create-and-populate-an-investigation","title":"Create and populate an investigation","text":"

              Investigations are created in 2 ways:

              • automatically:
              • if you scan multiple observables at the same time, a new investigation will be created by default and all the observables they will be automatically connected to the same investigation.
              • if you run a Job with a Playbook which contains a Pivot that triggers another Job, a new investigation will be created and both the Jobs will be added to the same investigation. See how you can create a new Pivot manually from the GUI.
              • manually: by clicking on the button in the \"History\" section you can create an Investigation from scratch without any job attached (see following image)

              If you want to add a job to an Investigation, you should click to the root block of the Investigation (see following image):

              Once a job has been added, you'll have something like this:

              If you want to remove a Job, you can click on the Job block and click \"Remove branch\". On the contrary, if you just want to see Job Results, you can click in the \"Link\" button. (check next image)

              "},{"location":"IntelOwl/usage/#example-output-of-a-complex-investigation","title":"Example output of a complex investigation","text":""},{"location":"Submodules/GoIntelOwl/","title":"go-intelowl","text":"

              go-intelowl is a client library/SDK that allows developers to easily automate and integrate IntelOwl with their own set of tools!

              "},{"location":"Submodules/GoIntelOwl/#table-of-contents","title":"Table of Contents","text":"
              • go-intelowl
              • Getting Started
                • Pre requisites
                • Installation
                • Usage
                • Examples
              • Contribute
              • License
              • Links
              • FAQ
                • Generate API key
                  • v4.0 and above
                  • v4.0 below
              "},{"location":"Submodules/GoIntelOwl/#getting-started","title":"Getting Started","text":""},{"location":"Submodules/GoIntelOwl/#pre-requisites","title":"Pre requisites","text":"
              • Go 1.17+
              "},{"location":"Submodules/GoIntelOwl/#installation","title":"Installation","text":"

              Use go get to retrieve the SDK to add it to your GOPATH workspace, or project's Go module dependencies.

              $ go get github.com/intelowlproject/go-intelowl\n
              "},{"location":"Submodules/GoIntelOwl/#usage","title":"Usage","text":"

              This library was built with ease of use in mind! Here are some quick examples to get you started. If you need more example you can go to the examples directory

              To start using the go-intelowl library you first need to import it:

              import \"github.com/intelowlproject/go-intelowl/gointelowl\"\n
              Construct a new IntelOwlClient, then use the various services to easily access different parts of Intelowl's REST API. Here's an example of getting all jobs:

              clientOptions := gointelowl.IntelOwlClientOptions{\n    Url:         \"your-cool-URL-goes-here\",\n    Token:       \"your-super-secret-token-goes-here\",\n    // This is optional\n    Certificate: \"your-optional-certificate-goes-here\",\n}\n\nintelowl := gointelowl.NewIntelOwlClient(\n    &clientOptions,\n    nil\n)\n\nctx := context.Background()\n\n// returns *[]Jobs or an IntelOwlError!\njobs, err := intelowl.JobService.List(ctx)\n
              For easy configuration and set up we opted for options structs. Where we can customize the client API or service endpoint to our liking! For more information go here. Here's a quick example!

              // ...Making the client and context!\n\ntagOptions = gointelowl.TagParams{\n  Label: \"NEW TAG\",\n  Color: \"#ffb703\",\n}\n\ncreatedTag, err := intelowl.TagService.Create(ctx, tagOptions)\nif err != nil {\n    fmt.Println(err)\n} else {\n    fmt.Println(createdTag)\n}\n
              "},{"location":"Submodules/GoIntelOwl/#examples","title":"Examples","text":"

              The examples directory contains a couple for clear examples, of which one is partially listed here as well:

              package main\n\nimport (\n    \"fmt\"\n\n    \"github.com/intelowlproject/go-intelowl/gointelowl\"\n)\n\nfunc main(){\n    intelowlOptions := gointelowl.IntelOwlClientOptions{\n        Url:         \"your-cool-url-goes-here\",\n        Token:       \"your-super-secret-token-goes-here\",\n        Certificate: \"your-optional-certificate-goes-here\",\n    }   \n\n    client := gointelowl.NewIntelOwlClient(\n        &intelowlOptions,\n        nil,\n    )\n\n    ctx := context.Background()\n\n    // Get User details!\n    user, err := client.UserService.Access(ctx)\n    if err != nil {\n        fmt.Println(\"err\")\n        fmt.Println(err)\n    } else {\n        fmt.Println(\"USER Details\")\n        fmt.Println(*user)\n    }\n}\n
              For complete usage of go-intelowl, see the full package docs.

              "},{"location":"Submodules/GoIntelOwl/#contribute","title":"Contribute","text":"

              If you want to follow the updates, discuss, contribute, or just chat then please join our slack channel we'd love to hear your feedback!

              "},{"location":"Submodules/GoIntelOwl/#license","title":"License","text":"

              Licensed under the GNU AFFERO GENERAL PUBLIC LICENSE.

              "},{"location":"Submodules/GoIntelOwl/#links","title":"Links","text":"
              • Intelowl
              • Documentation
              • API documentation
              • Examples
              "},{"location":"Submodules/GoIntelOwl/#faq","title":"FAQ","text":""},{"location":"Submodules/GoIntelOwl/#generate-api-key","title":"Generate API key","text":"

              You need a valid API key to interact with the IntelOwl server.

              "},{"location":"Submodules/GoIntelOwl/#v40-and-above","title":"v4.0 and above","text":"

              You can get an API by doing the following: 1. Log / Signin into intelowl 2. At the upper right click on your profile from the drop down select API Access/ Sessions 3. Then generate an API key or see it!

              "},{"location":"Submodules/GoIntelOwl/#v40-below","title":"v4.0 below","text":"

              Keys should be created from the admin interface of IntelOwl: you have to go in the Durin section (click on Auth tokens) and generate a key there.

              "},{"location":"Submodules/GoIntelOwl/examples/basicExample/example/","title":"Example","text":"

              This example will show you how to do a basic scan!

              "},{"location":"Submodules/GoIntelOwl/examples/client/client/","title":"Client","text":""},{"location":"Submodules/GoIntelOwl/examples/client/client/#client","title":"Client","text":"

              A good client is a client that is easy to use, configurable and customizable to a user\u2019s liking. Hence, the client has 4 great features: 1. Configurable HTTP client 2. Customizable timeouts 3. Logger 4. Easy ways to create the IntelOwlClient

              "},{"location":"Submodules/GoIntelOwl/examples/client/client/#configurable-http-client","title":"Configurable HTTP client","text":"

              Now from the documentation, you can see you can pass your http.Client. This is to facilitate each user\u2019s requirement and taste! If you don\u2019t pass one (nil) a default http.Client will be made for you!

              "},{"location":"Submodules/GoIntelOwl/examples/client/client/#customizable-timeouts","title":"Customizable timeouts","text":"

              From IntelOwlClientOptions you can add your own timeout to your requests as well.

              "},{"location":"Submodules/GoIntelOwl/examples/client/client/#logger","title":"Logger","text":"

              To ease developers' work go-intelowl provides a logger for easy debugging and tracking! For the logger we used logrus because of 2 reasons: 1. Easy to use 2. Extensible to your liking

              "},{"location":"Submodules/GoIntelOwl/examples/client/client/#easy-ways-to-create-the-intelowlclient","title":"Easy ways to create the IntelOwlClient","text":"

              As you know working with Golang structs is sometimes cumbersome we thought we could provide a simple way to create the client in a way that helps speed up development. This gave birth to the idea of using a JSON file to create the IntelOwlClient. The method NewIntelOwlClientThroughJsonFile does exactly that. Send the IntelOwlClientOptions JSON file path with your http.Client and LoggerParams in this method and you'll get the IntelOwlClient!

              "},{"location":"Submodules/GoIntelOwl/examples/optionalParams/optionalParams/","title":"Optional Parameters","text":"

              For the sake of simplicity, we decided that for some endpoints we\u2019ll be passing Option Parameters this is to facilitate easy access, configuration and automation so that you don\u2019t need to pass in many parameters but just a simple struct that can be easily converted to and from JSON!

              For example, let us look at the TagParams we use it as an argument for a method Create for TagService. From a glance, the TagParams look simple. They hold 2 fields: Label, and Color which can be passed seperatly to the method but imagine if you have many fields! (if you don\u2019t believe see the ObservableAnalysisParams)

              For a practical implementation you can see the example

              "},{"location":"Submodules/GoIntelOwl/tests/CONTRIBUTING/","title":"CONTRIBUTING","text":""},{"location":"Submodules/GoIntelOwl/tests/CONTRIBUTING/#how-unit-tests-were-written","title":"How unit tests were written","text":"

              The unit tests were written as a combination of table driven tests and the approach used by go-github

              Firstly we use a TestData struct that has the following fields: 1. Input - this is an interface as it is to be used as the input required for an endpoint 2. Data - this is a string as it'll be the JSON string that the endpoint is expected to return 2. StatusCode - this is an int as it is meant to be used as the expected response returned by the endpoint 3. Want - the expected struct that the method will return

              Now the reason we made this was that these fields were needed for every endpoint hence combining them into a single struct provided us reusability and flexibility.

              Now the testing suite used go's httptest library where we use httptest.Server as this setups a test server so that we can easily mock it. We also use http.ServerMux to mock our endpoints response.

              "},{"location":"Submodules/GoIntelOwl/tests/CONTRIBUTING/#how-to-add-a-new-test-for-an-endpoint","title":"How to add a new test for an endpoint","text":"

              Lets say IntelOwl added a new endpoint called supercool in Tag. Now you've implemented the endpoint as a method of TagService and now you want to add its unit tests.

              First go to tagService_test.go in the tests directory and add

              func TestSuperCoolEndPoint(t *testing.T) {\n    testCases := make(map[string]TestData)\n    testCases[\"simple\"] = TestData{\n        Input:      nil,\n        Data:       `{ \"supercool\": \"you're a great developer :)\"}`,\n        StatusCode: http.StatusOK,\n        Want: \"you're a great developer :)\",\n    }\n    for name, testCase := range testCases {\n        // subtest\n        t.Run(name, func(t *testing.T) {\n            // setup will give you the client, mux/router, closeServer\n            client, apiHandler, closeServer := setup()\n            defer closeServer()\n            ctx := context.Background()\n            // now you can use apiHandler to mock how the server will handle this endpoints request\n            // you can use mux/router's Handle method or HandleFunc\n            apiHandler.Handle(\"/api/tag/supercool\", func(w http.ResponseWriter, r *http.Request) {\n                // this is a helper test to check if it is the expected request sent by the client\n                testMethod(t, r, \"GET\")\n                w.Write([]byte(testCase.Data))\n            })\n            expectedRespone, err := client.TagService.SuperCool(ctx)\n            if err != nil {\n                testError(t, testCase, err)\n            } else {\n                testWantData(t, testCase.Want, expectedRespone)\n            }\n        })\n    }\n}\n

              Great! Now you've added your own unit tests.

              "},{"location":"Submodules/GreedyBear/","title":"Index","text":""},{"location":"Submodules/GreedyBear/#greedybear","title":"GreedyBear","text":"

              The project goal is to extract data of the attacks detected by a TPOT or a cluster of them and to generate some feeds that can be used to prevent and detect attacks.

              Official announcement here.

              "},{"location":"Submodules/GreedyBear/#documentation","title":"Documentation","text":"

              Documentation about GreedyBear installation, usage, configuration and contribution can be found at this link

              "},{"location":"Submodules/GreedyBear/#public-feeds","title":"Public feeds","text":"

              There are public feeds provided by The Honeynet Project in this site. Example

              Please do not perform too many requests to extract feeds or you will be banned.

              If you want to be updated regularly, please download the feeds only once every 10 minutes (this is the time between each internal update).

              To check all the available feeds, Please refer to our usage guide

              "},{"location":"Submodules/GreedyBear/#enrichment-service","title":"Enrichment Service","text":"

              GreedyBear provides an easy-to-query API to get the information available in GB regarding the queried observable (domain or IP address).

              To understand more, Please refer to our usage guide

              "},{"location":"Submodules/GreedyBear/#run-greedybear-on-your-environment","title":"Run Greedybear on your environment","text":"

              The tool has been created not only to provide the feeds from The Honeynet Project's cluster of TPOTs.

              If you manage one or more T-POTs of your own, you can get the code of this application and run Greedybear on your environment. In this way, you are able to provide new feeds of your own.

              To install it locally, Please refer to our installation guide

              "},{"location":"Submodules/GreedyBear/#sponsors","title":"Sponsors","text":""},{"location":"Submodules/GreedyBear/#certego","title":"Certego","text":"

              Certego is a MDR (Managed Detection and Response) and Threat Intelligence Provider based in Italy.

              Started as a personal Christmas project from Matteo Lodi, since then GreedyBear is being improved mainly thanks to the efforts of the Certego Threat Intelligence Team.

              "},{"location":"Submodules/GreedyBear/#the-honeynet-project","title":"The Honeynet Project","text":"

              The Honeynet Project is a non-profit organization working on creating open source cyber security tools and sharing knowledge about cyber threats.

              "},{"location":"Submodules/GreedyBear/FEEDS_LICENSE/","title":"FEEDS LICENSE","text":"

              The data provided from the site https://greedybear.honeynet.org are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

              "},{"location":"Submodules/GreedyBear/frontend/","title":"GreedyBear - frontend","text":"

              Built with @certego/certego-ui.

              "},{"location":"Submodules/GreedyBear/frontend/#design-thesis","title":"Design thesis","text":"
              • Re-usable components/hooks/stores that other projects can also benefit from should be added to certego-ui package.
              • GreedyBear specific:
              • components should be added to src/components.
              • general hooks should be added to src/hooks.
              • zustand stores hooks should be added to src/stores.
              "},{"location":"Submodules/GreedyBear/frontend/#directory-structure","title":"Directory Structure","text":"
              public/                                   public static assets\n|- icons/                                 icons/favicon\n|- index.html/                            root HTML file\nsrc/                                      source code\n|- components/                            pages and components\n|  |- auth/                               `certego_saas.apps.auth` (login, logout pages)\n|  |- dashboard/                          dashboard page and charts\n|  |- home/                               landing/home page\n|  |- Routes.jsx                          lazy route-component mappings\n|- constants/                             constant values\n|  |- api.js                              API URLs\n|  |- environment.js                      environment variables\n|  |- index.js                            GreedyBear specific constants\n|- hooks/                                 react hooks\n|- layouts/                               header, main, footer containers\n|- stores/                                zustand stores hooks\n|- styles/                                scss files\n|- wrappers/                              Higher-Order components\n|- App.jsx                                App component\n|- index.jsx                              Root JS file (ReactDOM renderer)\n
              "},{"location":"Submodules/GreedyBear/frontend/#local-development-environment","title":"Local Development Environment","text":"

              The frontend inside the docker containers does not hot-reload, so you need to use CRA dev server on your host machine to serve pages when doing development on the frontend, using docker nginx only as API source.

              • Start GreedyBear containers (see docs). Original dockerized app is accessible on http://localhost:80

              • If you have not node-js installed, you have to do that. Follow the guide here. We tested this with NodeJS >=16.6

              • Install npm packages locally

              cd ./frontend && npm install\n
              • Start CRA dev server:
              npm start\n
              • Now you can access the auto-reloading frontend on http://localhost:3001. It acts as proxy for API requests to original app web server.

              • JS app main configs are available in package.json and enviroments.js.

              "},{"location":"Submodules/GreedyBear/frontend/#external-docs","title":"External Docs","text":"
              • Create React App documentation.
              • React documentation.
              "},{"location":"Submodules/IntelOwl/","title":"Index","text":""},{"location":"Submodules/IntelOwl/#intel-owl","title":"Intel Owl","text":"

              Do you want to get threat intelligence data about a malware, an IP address or a domain? Do you want to get this kind of data from multiple sources at the same time using a single API request?

              You are in the right place!

              IntelOwl is an Open Source solution for management of Threat Intelligence at scale. It integrates a number of analyzers available online and a lot of cutting-edge malware analysis tools.

              "},{"location":"Submodules/IntelOwl/#features","title":"Features","text":"

              This application is built to scale out and to speed up the retrieval of threat info.

              It provides: - Enrichment of Threat Intel for files as well as observables (IP, Domain, URL, hash, etc). - A Fully-fledged REST APIs written in Django and Python. - An easy way to be integrated in your stack of security tools to automate common jobs usually performed, for instance, by SOC analysts manually. (Thanks to the official libraries pyintelowl and go-intelowl) - A built-in GUI: provides features such as dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, etc. - A framework composed of modular components called Plugins: - analyzers that can be run to either retrieve data from external sources (like VirusTotal or AbuseIPDB) or to generate intel from internally available tools (like Yara or Oletools) - connectors that can be run to export data to external platforms (like MISP or OpenCTI) - pivots that are designed to trigger the execution of a chain of analysis and connect them to each other - visualizers that are designed to create custom visualizations of analyzers results - ingestors that allows to automatically ingest stream of observables or files to IntelOwl itself - playbooks that are meant to make analysis easily repeatable

              "},{"location":"Submodules/IntelOwl/#documentation","title":"Documentation","text":"

              We try hard to keep our documentation well written, easy to understand and always updated. All info about installation, usage, configuration and contribution can be found here

              "},{"location":"Submodules/IntelOwl/#publications-and-media","title":"Publications and Media","text":"

              To know more about the project and its growth over time, you may be interested in reading the official blog posts and/or videos about the project by clicking on this link

              "},{"location":"Submodules/IntelOwl/#available-services-or-analyzers","title":"Available services or analyzers","text":"

              You can see the full list of all available analyzers in the documentation.

              Type Analyzers Available Inbuilt modules - Static Office Document, RTF, PDF, PE File Analysis and metadata extraction - Strings Deobfuscation and analysis (FLOSS, Stringsifter, ...) - PE Emulation with Qiling and Speakeasy - PE Signature verification - PE Capabilities Extraction (CAPA) - Javascript Emulation (Box-js) - Android Malware Analysis (Quark-Engine, ...) - SPF and DMARC Validator - Yara (a lot of public rules are available. You can also add your own rules) - more... External services - Abuse.ch MalwareBazaar/URLhaus/Threatfox/YARAify - GreyNoise v2 - Intezer - VirusTotal v3 - Crowdsec - URLscan - Shodan - AlienVault OTX - Intelligence_X - MISP - many more.."},{"location":"Submodules/IntelOwl/#partnerships-and-sponsors","title":"Partnerships and sponsors","text":"

              As open source project maintainers, we strongly rely on external support to get the resources and time to work on keeping the project alive, with a constant release of new features, bug fixes and general improvements.

              Because of this, we joined Open Collective to obtain non-profit equal level status which allows the organization to receive and manage donations transparently. Please support IntelOwl and all the community by choosing a plan (BRONZE, SILVER, etc).

              "},{"location":"Submodules/IntelOwl/#gold","title":"\ud83e\udd47 GOLD","text":""},{"location":"Submodules/IntelOwl/#certego","title":"Certego","text":"

              Certego is a MDR (Managed Detection and Response) and Threat Intelligence Provider based in Italy.

              IntelOwl was born out of Certego's Threat intelligence R&D division and is constantly maintained and updated thanks to them.

              "},{"location":"Submodules/IntelOwl/#the-honeynet-project","title":"The Honeynet Project","text":"

              The Honeynet Project is a non-profit organization working on creating open source cyber security tools and sharing knowledge about cyber threats.

              Thanks to Honeynet, we are hosting a public demo of the application here. If you are interested, please contact a member of Honeynet to get access to the public service.

              "},{"location":"Submodules/IntelOwl/#google-summer-of-code","title":"Google Summer of Code","text":"

              Since its birth this project has been participating in the Google Summer of Code (GSoC)!

              If you are interested in participating in the next Google Summer of Code, check all the info available in the dedicated repository!

              "},{"location":"Submodules/IntelOwl/#silver","title":"\ud83e\udd48 SILVER","text":""},{"location":"Submodules/IntelOwl/#threathunterai","title":"ThreatHunter.ai","text":"

              ThreatHunter.ai\u00ae, is a 100% Service-Disabled Veteran-Owned Small Business started in 2007 under the name Milton Security Group. ThreatHunter.ai is the global leader in Dynamic Threat Hunting. Operating a true 24x7x365 Security Operation Center with AI/ML-enhanced human Threat Hunters, ThreatHunter.ai has changed the industry in how threats are found, and mitigated in real time. For over 15 years, our teams of Threat Hunters have stopped hundreds of thousands of threats and assisted organizations in defending against threat actors around the clock.

              "},{"location":"Submodules/IntelOwl/#bronze","title":"\ud83e\udd49 BRONZE","text":""},{"location":"Submodules/IntelOwl/#docker","title":"Docker","text":"

              In 2021 IntelOwl joined the official Docker Open Source Program. This allows IntelOwl developers to easily manage Docker images and focus on writing the code. You may find the official IntelOwl Docker images here.

              "},{"location":"Submodules/IntelOwl/#digitalocean","title":"DigitalOcean","text":"

              In 2022 IntelOwl joined the official DigitalOcean Open Source Program.

              "},{"location":"Submodules/IntelOwl/#other-collaborations","title":"Other collaborations","text":"
              • LimaCharlie
              • Tines
              "},{"location":"Submodules/IntelOwl/#about-the-author-and-maintainers","title":"About the author and maintainers","text":"

              Feel free to contact the main developers at any time on Twitter:

              • Matteo Lodi: Author and principal maintainer
              • Simone Berni: Backend Maintainer
              • Daniele Rosetti: Frontend Maintainer
              • Eshaan Bansal: Key Contributor
              "},{"location":"Submodules/IntelOwl/docker/bin/","title":"Embedded binary list","text":""},{"location":"Submodules/IntelOwl/docker/bin/#osslsigncode","title":"Osslsigncode","text":"

              We embedded the compiled version for Ubuntu for that can be retrieved from its original repo here.

              We decided to do not use the version shipped by default Ubuntu packages because it were too old (2.1)

              At the last time of writing we uploaded the version 2.6-dev

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/","title":"Advanced Configuration","text":"

              This page includes details about some advanced features that Intel Owl provides which can be optionally configured by the administrator.

              • ElasticSearch
                • Kibana
                • Example Configuration
                • Business Intelligence
              • Authentication options
                • OAuth support
                • LDAP
                • RADIUS
              • OpenCTI support
              • Cloud Support
                • AWS support
                • Secrets
                • SQS
                • S3
                • SES
                • Google Kubernetes Engine
              • Queues
                • Multi Queue
                • Queue Customization
                • Queue monitoring
              • Manual usage
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#elasticsearch","title":"ElasticSearch","text":"

              Right now only ElasticSearch v8 is supported.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#dsl","title":"DSL","text":"

              IntelOwl makes use of django-elasticsearch-dsl to index Job results into elasticsearch. The save and delete operations are auto-synced so you always have the latest data in ES.

              In the env_file_app_template, you'd see various elasticsearch related environment variables. The user should spin their own Elastic Search instance and configure these variables.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#kibana","title":"Kibana","text":"

              Intel Owl provides a Kibana's \"Saved Object\" configuration (with example dashboard and visualizations). It can be downloaded from here and can be imported into Kibana by going to the \"Saved Objects\" panel (http://localhost:5601/app/management/kibana/objects).

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#example-configuration","title":"Example Configuration","text":"
              1. Setup Elastic Search and Kibana and say it is running in a docker service with name elasticsearch on port 9200 which is exposed to the shared docker network. (Alternatively, you can spin up a local Elastic Search instance, by appending --elastic to the ./start command. Note that the local Elastic Search instance consumes large amount of memory, and hence having >=16GB is recommended.))
              2. In the env_file_app, we set ELASTICSEARCH_DSL_ENABLED to True and ELASTICSEARCH_DSL_HOST to elasticsearch:9200.
              3. Now start the docker containers and execute
              docker exec -ti intelowl_uwsgi python manage.py search_index --rebuild\n

              This will build and populate all existing job objects into the jobs index.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#business-intelligence","title":"Business Intelligence","text":"

              IntelOwl makes use of elasticsearch-py to store data that can be used for Business Intelligence purpose. Since plugin reports are deleted periodically, this feature allows to save indefinitely small amount of data to keep track of how analyzers perform and user usage. At the moment, the following information are sent to elastic: - application name - timestamp - username - configuration used - process_time - status - end_time - parameters

              Documents are saved in the ELEASTICSEARCH_BI_INDEX-%YEAR-%MONTH, allowing to manage the retention accordingly. To activate this feature, it is necessary to set ELASTICSEARCH_BI_ENABLED to True in the env_file_app and ELASTICSEARCH_BI_HOST to elasticsearch:9200 or your elasticsearch server.

              An index template is created after the first bulk submission of reports. If you want to use kibana to visualize your data/make dashboard, you must create an index pattern: Go to Kibana -> Discover -> Stack Management -> Index Patterns -> search for your index and use as time field timestamp

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#authentication-options","title":"Authentication options","text":"

              IntelOwl provides support for some of the most common authentication methods:

              • Google Oauth2
              • LDAP
              • RADIUS
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#google-oauth2","title":"Google OAuth2","text":"

              The first step is to create a Google Cloud Platform project, and then create OAuth credentials for it.

              It is important to add the correct callback in the \"Authorized redirect URIs\" section to allow the application to redirect properly after the successful login. Add this:

              http://<localhost|yourowndomain>/api/auth/google-callback\n

              After that, specify the client ID and secret as GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables and restart IntelOwl to see the applied changes.

              Note

              While configuring Google Auth2 you can choose either to enable access to the all users with a Google Account (\"External\" mode) or to enable access to only the users of your organization (\"Internal\" mode). Reference"},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#ldap","title":"LDAP","text":"

              IntelOwl leverages Django-auth-ldap to perform authentication via LDAP.

              How to configure and enable LDAP on Intel Owl?

              1. Change the values with your LDAP configuration inside configuration/ldap_config.py. This file is mounted as a docker volume, so you won't need to rebuild the image.

              Note

              For more details on how to configure this file, check the official documentation of the django-auth-ldap library.
              1. Once you have done that, you have to set the environment variable LDAP_ENABLED as True in the environment configuration file env_file_app. Finally, you can restart the application with docker-compose up
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#radius-authentication","title":"RADIUS Authentication","text":"

              IntelOwl leverages Django-radius to perform authentication via RADIUS server.

              How to configure and enable RADIUS authentication on Intel Owl?

              1. Change the values with your RADIUS auth configuration inside configuration/radius_config.py. This file is mounted as a docker volume, so you won't need to rebuild the image.

              Note

              For more details on how to configure this file, check the official documentation of the django-radius library.
              1. Once you have done that, you have to set the environment variable RADIUS_AUTH_ENABLED as True in the environment configuration file env_file_app. Finally, you can restart the application with docker-compose up
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#opencti","title":"OpenCTI","text":"

              Like many other integrations that we have, we have an Analyzer and a Connector for the OpenCTI platform.

              This allows the users to leverage these 2 popular open source projects and frameworks together.

              So why we have a section here? This is because there are various compatibility problems with the official PyCTI library.

              We found out (see issues in IntelOwl and PyCTI) that, most of the times, it is required that the OpenCTI version of the server you are using and the pycti version installed in IntelOwl must match perfectly.

              Because of that, we decided to provide to the users the chance to customize the version of PyCTI installed in IntelOwl based on the OpenCTI version that they are using.

              To do that, you would need to leverage the option --pycti-version provided by the ./start helper: * check the default version that would be installed by checking the description of the option --pycti-version with ./start -h * if the default version is different from your OpenCTI server version, you need to rebuild the IntelOwl Image with ./start test build --pycti-version <your_version> * then restart the project ./start test up -- --build * enjoy

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#cloud-support","title":"Cloud Support","text":""},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#aws-support","title":"AWS support","text":"

              We have support for several AWS services.

              You can customize the AWS Region location of you services by changing the environment variable AWS_REGION. Default is eu-central-1

              You have to add some credentials for AWS: if you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials: to allow that just set AWS_IAM_ACCESS to True. If that is not the case, you have to set both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#s3","title":"S3","text":"

              If you prefer to use S3 to store the analyzed samples, instead of the local storage, you can do it.

              First, you need to configure the environment variable LOCAL_STORAGE to False to enable it and set AWS_STORAGE_BUCKET_NAME to the AWS bucket you want to use.

              Then you need to configure permission access to the chosen S3 bucket.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#message-broker","title":"Message Broker","text":"

              IntelOwl at the moment supports 3 different message brokers: - Redis (default) - RabbitMQ - Aws SQS

              The default broker, if nothing is specified, is Redis.

              To use RabbitMQ, you must use the option --rabbitmq when launching IntelOwl with the ./start script.

              To use Aws SQS, you must use the option --sqs when launching IntelOwl with the .start script. In that case, you should create new SQS queues in AWS called intelowl-<environment>-<queue_name> and give your instances on AWS the proper permissions to access it. Moreover, you must populate the AWS_USER_NUMBER. This is required to connect in the right way to the selected SQS queues. Only FIFO queues are supported.

              If you want to use a remote message broker (like an ElasticCache or AmazonMQ instance), you must populate the BROKER_URL environment variable.

              It is possible to use task priority inside IntelOwl: each User has default priority of 10, and robots users (like the Ingestors) have a priority of 7. You can customize these priorities inside Django Admin, in the Authentication.User Profiles section.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#websockets","title":"Websockets","text":"

              Redis is used for two different functions: - message broker - websockets

              For this reason, a Redis instance is mandatory. You can personalize IntelOwl in two different way: - with a local Redis instance.

              This is the default behaviour.

              • With a remote Redis instance.

              You must use the option --use-external-redis when launching IntelOwl with the .start script. Moreover, you need to populate the WEBSOCKETS_URL environment variable. If you are using Redis as a message broker too, remember to populate the BROKER_URL environment variable

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#rds","title":"RDS","text":"

              If you like, you could use AWS RDS instead of PostgreSQL for your database. In that case, you should change the database required options accordingly: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD and setup your machine to access the service.

              If you have IntelOwl deployed on the AWS infrastructure, you can use IAM credentials to access the Postgres DB. To allow that just set AWS_RDS_IAM_ROLE to True. In this case DB_PASSWORD is not required anymore.

              Moreover, to avoid to run PostgreSQL locally, you would need to use the option --use-external-database when launching IntelOwl with the ./start script.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#ses","title":"SES","text":"

              If you like, you could use Amazon SES for sending automated emails (password resets / registration requests, etc).

              You need to configure the environment variable AWS_SES to True to enable it.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#secrets","title":"Secrets","text":"

              You can use the \"Secrets Manager\" to store your credentials. In this way your secrets would be better protected.

              Instead of adding the variables to the environment file, you should just add them with the same name on the AWS Secrets Manager and Intel Owl will fetch them transparently.

              Obviously, you should have created and managed the permissions in AWS in advance and accordingly to your infrastructure requirements.

              Also, you need to set the environment variable AWS_SECRETS to True to enable this mode.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#nfs","title":"NFS","text":"

              You can use a Network File System for the shared_files that are downloaded runtime by IntelOwl (for example Yara rules).

              To use this feature, you would need to add the address of the remote file system inside the .env file, and you would need to use the option --nfs when launching IntelOwl with the ./start script.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#google-kubernetes-engine","title":"Google Kubernetes Engine","text":"

              Right now there is no official support for Kubernetes deployments.

              But we have an active community. Please refer to the following blog post for an example on how to deploy IntelOwl on Google Kubernetes Engine:

              Deploying Intel-Owl on GKE by Mayank Malik.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#queues","title":"Queues","text":""},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#multi-queue","title":"Multi Queue","text":"

              IntelOwl provides an additional multi-queue.override.yml compose file allowing IntelOwl users to better scale with the performance of their own architecture.

              If you want to leverage it, you should add the option --multi-queue when starting the project. Example:

              ./start prod up --multi-queue\n

              This functionality is not enabled by default because this deployment would start 2 more containers so the resource consumption is higher. We suggest to use this option only when leveraging IntelOwl massively.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#queue-customization","title":"Queue Customization","text":"

              It is possible to define new celery workers: each requires the addition of a new container in the docker-compose file, as shown in the multi-queue.override.yml.

              Moreover IntelOwl requires that the name of the workers are provided in the docker-compose file. This is done through the environment variable CELERY_QUEUES inside the uwsgi container. Each queue must be separated using the character ,, as shown in the example.

              One can customize what analyzer should use what queue by specifying so in the analyzer entry in the analyzer_config.json configuration file. If no queue(s) are provided, the default queue will be selected.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#queue-monitoring","title":"Queue monitoring","text":"

              IntelOwl provides an additional flower.override.yml compose file allowing IntelOwl users to use Flower features to monitor and manage queues and tasks

              If you want to leverage it, you should add the option --flower when starting the project. Example:

              ./start prod up --flower\n

              The flower interface is available at port 5555: to set the credentials for its access, update the environment variables

              FLOWER_USER\nFLOWER_PWD\n

              or change the .htpasswd file that is created in the docker directory in the intelowl_flower container.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#manual-usage","title":"Manual Usage","text":"

              The ./start script essentially acts as a wrapper over Docker Compose, performing additional checks. IntelOwl can still be started by using the standard docker compose command, but all the dependencies have to be manually installed by the user.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#options","title":"Options","text":"

              The --project-directory and -p options are required to run the project. Default values set by ./start script are \"docker\" and \"intel_owl\", respectively.

              The startup is based on chaining various Docker Compose YAML files using -f option. All Docker Compose files are stored in docker/ directory of the project. The default compose file, named default.yml, requires configuration for an external database and message broker. In their absence, the postgres.override.yml and rabbitmq.override.yml files should be chained to the default one.

              The command composed, considering what is said above (using sudo), is

              sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -p intel_owl up\n

              The other most common compose file that can be used is for the testing environment. The equivalent of running ./start test up is adding the test.override.yml file, resulting in:

              sudo docker compose --project-directory docker -f docker/default.yml -f docker/postgres.override.yml -f docker/rabbitmq.override.yml -f docker/test.override.yml -p intel_owl up\n

              All other options available in the ./start script (./start -h to view them) essentially chain other compose file to docker compose command with corresponding filenames.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Configuration/#optional-analyzer","title":"Optional Analyzer","text":"

              IntelOwl includes integrations with some analyzer that are not enabled by default. These analyzers, stored under the integrations/ directory, are packed within Docker Compose files. The compose.yml file has to be chained to include the analyzer. The additional compose-test.yml file has to be chained for testing environment.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/","title":"Advanced Usage","text":"

              This page includes details about some advanced features that Intel Owl provides which can be optionally enabled. Namely,

              • Advanced Usage
              • Organizations and User Management
                • Multi Tenancy
                • Registration
              • Optional Analyzers
              • Customize analyzer execution
                • from the GUI
                • from Pyintelowl
                • CyberChef
                • PhoneInfoga
              • Analyzers with special configuration
              • Notifications
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#organizations-and-user-management","title":"Organizations and User management","text":"

              Starting from IntelOwl v4, a new \"Organization\" section is available on the GUI. This section substitute the previous permission management via Django Admin and aims to provide an easier way to manage users and visibility.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#multi-tenancy","title":"Multi Tenancy","text":"

              Thanks to the \"Organization\" feature, IntelOwl can be used by multiple SOCs, companies, etc...very easily. Right now it works very simply: only users in the same organization can see analysis of one another. An user can belong to an organization only.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#manage-organizations","title":"Manage organizations","text":"

              You can create a new organization by going to the \"Organization\" section, available under the Dropdown menu you cand find under the username.

              Once you create an organization, you are the unique \"Owner\" of that organization. So you are the only one who can delete the organization and promote/demote/kick users. Another role, which is called \"Admin\", can be set to a user (via the Django Admin interface only for now). Owners and admins share the following powers: they can manage invitations and the organization's plugin configuration.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#accept-invites","title":"Accept Invites","text":"

              Once an invite has sent, the invited user has to login, go to the \"Organization\" section and accept the invite there. Afterwards the Administrator will be able to see the user in his \"Organization\" section.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#plugins-params-and-secrets","title":"Plugins Params and Secrets","text":"

              From IntelOwl v4.1.0, Plugin Parameters and Secrets can be defined at the organization level, in the dedicated section. This allows to share configurations between users of the same org while allowing complete multi-tenancy of the application. Only Owners and Admins of the organization can set, change and delete them.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#disable-plugins-at-org-level","title":"Disable Plugins at Org level","text":"

              The org admin can disable a specific plugin for all the users in a specific org. To do that, Org Admins needs to go in the \"Plugins\" section and click the button \"Enabled for organization\" of the plugin that they want to disable.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#registration","title":"Registration","text":"

              Since IntelOwl v4.2.0 we added a Registration Page that can be used to manage Registration requests when providing IntelOwl as a Service.

              After a user registration has been made, an email is sent to the user to verify their email address. If necessary, there are buttons on the login page to resend the verification email and to reset the password.

              Once the user has verified their email, they would be manually vetted before being allowed to use the IntelOwl platform. The registration requests would be handled in the Django Admin page by admins. If you have IntelOwl deployed on an AWS instance with an IAM role you can use the SES service.

              To have the \"Registration\" page to work correctly, you must configure some variables before starting IntelOwl. See Optional Environment Configuration

              In a development environment the emails that would be sent are written to the standard output.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#recaptcha-configuration","title":"Recaptcha configuration","text":"

              The Registration Page contains a Recaptcha form from Google. By default, that Recaptcha is not configured and is not shown.

              If your intention is to publish IntelOwl as a Service you should first remember to comply to the AGPL License.

              Then you need to add the generated Recaptcha Secret in the RECAPTCHA_SECRET_KEY value in the env_file_app file.

              Afterwards you should configure the Recaptcha Key for your site and add that value in the RECAPTCHA_SITEKEY in the frontend/public/env.js file. In that case, you would need to re-build the application to have the changes properly reflected.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#optional-analyzers","title":"Optional Analyzers","text":"

              Some analyzers which run in their own Docker containers are kept disabled by default. They are disabled by default to prevent accidentally starting too many containers and making your computer unresponsive.

              Name Analyzers Description Malware Tools Analyzers
              • PEframe_Scan
              • Capa_Info
              • Floss
              • Strings_Info
              • ClamAV
              • APKiD
              • Thug_URL_Info, Thug_HTML_Info
              • BoxJS
              • Qiling_Windows, Qiling_Windows_Shellcode, Qiling_Linux, Qiling_Linux_Shellcode
              • PEFrame performs static analysis on Portable Executable malware and malicious MS Office documents
              • Capa detects capabilities in executable files
              • FLOSS automatically deobfuscate strings from malware binaries
              • String_Info_Classic extracts human-readable strings where as ML version of it ranks them
              • ClamAV antivirus engine scans files for trojans, viruses, malwares using a multi-threaded daemon
              • APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.
              • Thug performs hybrid dynamic/static analysis on a URL or HTML page.
              • Box-JS is a tool for studying JavaScript malware
              • Qiling is a tool for emulating the execution of a binary file or a shellcode. It requires the configuration of its rootfs, and the optional configuration of profiles. The rootfs can be copied from the Qiling project: please remember that Windows dll must be manually added for license reasons. Qiling provides a DllCollector to retrieve dlls from your licensed Windows. Profiles must be placed in the profiles subfolder
              TOR Analyzers Onionscan Scans TOR .onion domains for privacy leaks and information disclosures. CyberChef CyberChef Run a transformation on a CyberChef server using pre-defined or custom recipes(rules that describe how the input has to be transformed). Check further instructions here PCAP Analyzers Suricata You can upload a PCAP to have it analyzed by Suricata with the open Ruleset. The result will provide a list of the triggered signatures plus a more detailed report with all the raw data generated by Suricata. You can also add your own rules (See paragraph \"Analyzers with special configuration\"). The installation is optimized for scaling so the execution time is really fast. PhoneInfoga PhoneInfoga_scan PhoneInfoga is one of the most advanced tools to scan international phone numbers. It allows you to first gather basic information such as country, area, carrier and line type, then use various techniques to try to find the VoIP provider or identify the owner. It works with a collection of scanners that must be configured in order for the tool to be effective. PhoneInfoga doesn't automate everything, it's just there to help investigating on phone numbers. here

              To enable all the optional analyzers you can add the option --all_analyzers when starting the project. Example:

              ./start prod up --all_analyzers\n

              Otherwise you can enable just one of the cited integration by using the related option. Example:

              ./start prod up --tor_analyzers\n
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#customize-analyzer-execution","title":"Customize analyzer execution","text":"

              Some analyzers provide the chance to customize the performed analysis based on parameters that are different for each analyzer.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#from-the-gui","title":"from the GUI","text":"

              You can click on \"Runtime Configuration\" button in the \"Scan\" page and add the runtime configuration in the form of a dictionary. Example:

              \"VirusTotal_v3_File\": {\n    \"force_active_scan_if_old\": true\n}\n
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#from-pyintelowl","title":"from Pyintelowl","text":"

              While using send_observable_analysis_request or send_file_analysis_request endpoints, you can pass the parameter runtime_configuration with the optional values. Example:

              runtime_configuration = {\n    \"Doc_Info\": {\n        \"additional_passwords_to_check\": [\"passwd\", \"2020\"]\n    }\n}\npyintelowl_client.send_file_analysis_request(..., runtime_configuration=runtime_configuration)\n
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#phoneinfoga","title":"PhoneInfoga","text":"

              PhoneInfoga provides several Scanners to extract as much information as possible from a given phone number. Those scanners may require authentication, so they're automatically skipped when no authentication credentials are found.

              By default the scanner used is local. Go through this guide to initiate other required API keys related to this analyzer.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#cyberchef","title":"CyberChef","text":"

              You can either use pre-defined recipes or create your own as explained here.

              To use a pre-defined recipe, set the predefined_recipe_name argument to the name of the recipe as defined here. Else, leave the predefined_recipe_name argument empty and set the custom_recipe argument to the contents of the recipe you want to use.

              Additionally, you can also (optionally) set the output_type argument.

              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#pre-defined-recipes","title":"Pre-defined recipes","text":"
              • \"to decimal\": [{\"op\": \"To Decimal\", \"args\": [\"Space\", False]}]
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#analyzers-with-special-configuration","title":"Analyzers with special configuration","text":"

              Some analyzers could require a special configuration:

              • GoogleWebRisk: this analyzer needs a service account key with the Google Cloud credentials to work properly. You should follow the official guide for creating the key. Then you can populate the secret service_account_json for that analyzer with the JSON of the service account file.

              • ClamAV: this Docker-based analyzer uses clamd daemon as its scanner and is communicating with clamdscan utility to scan files. The daemon requires 2 different configuration files: clamd.conf(daemon's config) and freshclam.conf (virus database updater's config). These files are mounted as docker volumes in /integrations/malware_tools_analyzers/clamav and hence, can be edited by the user as per needs, without restarting the application. Moreover ClamAV is integrated with unofficial open source signatures extracted with Fangfrisch. The configuration file fangfrisch.conf is mounted in the same directory and can be customized on your wish. For instance, you should change it if you want to integrate open source signatures from SecuriteInfo

              • Suricata: you can customize the behavior of Suricata:

              • /integrations/pcap_analyzers/config/suricata/rules: here there are Suricata rules. You can change the custom.rules files to add your own rules at any time. Once you made this change, you need to either restart IntelOwl or (this is faster) run a new analysis with the Suricata analyzer and set the parameter reload_rules to true.

              • /integrations/pcap_analyzers/config/suricata/etc: here there are Suricata configuration files. Change it based on your wish. Restart IntelOwl to see the changes applied.

              • Yara:

              • You can customize both the repositories parameter and private_repositories secret to download and use different rules from the default that IntelOwl currently support.
                • The repositories values is what will be used to actually run the analysis: if you have added private repositories, remember to add the url in repositories too!
              • You can add local rules inside the directory at /opt/deploy/files_required/yara/YOUR_USERNAME/custom_rules/. Please remember that these rules are not synced in a cluster deploy: for this reason is advised to upload them on GitHub and use the repositories or private_repositories attributes.
              "},{"location":"Submodules/IntelOwl/docs/source/Advanced-Usage/#notifications","title":"Notifications","text":"

              Since v4, IntelOwl integrated the notification system from the certego_saas package, allowing the admins to create notification that every user will be able to see.

              The user would find the Notifications button on the top right of the page:

              There the user can read notifications provided by either the administrators or the IntelOwl Maintainers.

              As an Admin, if you want to add a notification to have it sent to all the users, you have to login to the Django Admin interface, go to the \"Notifications\" section and add it there. While adding a new notification, in the body section it is possible to even use HTML syntax, allowing to embed images, links, etc; in the app_name field, please remember to use intelowl as the app name.

              Everytime a new release is installed, once the backend goes up it will automatically create a new notification, having as content the latest changes described in the CHANGELOG.md, allowing the users to keep track of the changes inside intelowl itself.

              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/","title":"Contribute","text":"

              There are a lot of different ways you could choose to contribute to the IntelOwl Project:

              • main repository: IntelOwl

              • official Python client: pyintelowl.

              • official GO client: go-intelowl.

              • official IntelOwl Site: intelowlproject.github.io.

              • honeypots project: Greedybear

              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#rules","title":"Rules","text":"

              Intel Owl welcomes contributors from anywhere and from any kind of education or skill level. We strive to create a community of developers that is welcoming, friendly and right.

              For this reason it is important to follow some easy rules based on a simple but important concept: Respect.

              • Before asking any questions regarding how the project works, please read through all the documentation and install the project on your own local machine to try it and understand how it basically works. This is a form of respect to the maintainers.

              • DO NOT contact the maintainers with direct messages unless it is an urgent request. We don't have much time and cannot just answer to all the questions that we receive like \"Guide me please! Help me understand how the project work\". There is plenty of documentation and a lot of people in the community that can help you and would benefit from your questions. Share your problems and your knowledge. Please ask your questions in open channels (Github and Slack). This is a form of respect to the maintainers and to the community.

              • Before starting to work on an issue, you need to get the approval of one of the maintainers. Therefore please ask to be assigned to an issue. If you do not that but you still raise a PR for that issue, your PR can be rejected. This is a form of respect for both the maintainers and the other contributors who could have already started to work on the same problem.

              • When you ask to be assigned to an issue, it means that you are ready to work on it. When you get assigned, take the lock and then you disappear, you are not respecting the maintainers and the other contributors who could be able to work on that. So, after having been assigned, you have a week of time to deliver your first draft PR. After that time has passed without any notice, you will be unassigned.

              • Once you started working on an issue and you have some work to share and discuss with us, please raise a draft PR early with incomplete changes. This way you can continue working on the same and we can track your progress and actively review and help. This is a form of respect to you and to the maintainers.

              • When creating a PR, please read through the sections that you will find in the PR template and compile it appropriately. If you do not, your PR can be rejected. This is a form of respect to the maintainers.

              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#code-style","title":"Code Style","text":"

              Keeping to a consistent code style throughout the project makes it easier to contribute and collaborate. We make use of psf/black and isort for code formatting and flake8 for style guides.

              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-start-setup-project-and-development-instance","title":"How to start (Setup project and development instance)","text":"

              This guide assumes that you have already performed the steps required to install the project. If not, please do it (Installation Guide).

              Create a personal fork of the project on Github. Then, please create a new branch based on the develop branch that contains the most recent changes. This is mandatory.

              git checkout -b myfeature develop

              Then we strongly suggest to configure pre-commit to force linters on every commits you perform

              # From the project directory\npython3 -m venv venv\nsource venv/bin/activate\n# from the project base directory\npip install pre-commit\npre-commit install\n\n# create .env file for controlling repo_downloader.sh \n# (to speed up image builds during development: it avoid downloading some repos)\ncp docker/.env.start.test.template docker/.env.start.test\n\n# set STAGE env variable to \"local\"\nsed -i \"s/STAGE=\\\"production\\\"/STAGE=\\\"local\\\"/g\" docker/env_file_app\n
              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#backend","title":"Backend","text":"

              Now, you can execute IntelOwl in development mode by selecting the mode test while launching the startup script:

              ./start test up\n

              Every time you perform a change, you should perform an operation to reflect the changes into the application:

              • if you changed the python requirements, restart the application and re-build the images. This is the slowest process. You can always choose this way but it would waste a lot of time.
              ./start test down && ./start test up -- --build\n
              • if you changed either analyzers, connectors, playbooks or anything that is executed asynchronously by the \"celery\" containers, you just need to restart the application because we leverage Docker bind volumes that will reflect the changes to the containers. This saves the time of the build
              ./start test down && ./start test up\n
              • if you made changes to either the API or anything that is executed only by the application server, changes will be instantly reflected and you don't need to do anything. This is thanks to the Django Development server that is executed instead of uwsgi while using the test mode
              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#note-about-documentation","title":"NOTE about documentation:","text":"

              If you made any changes to an existing model/serializer/view, please run the following command to generate a new version of the API schema and docs:

              docker exec -it intelowl_uwsgi python manage.py spectacular --file docs/source/schema.yml && make html\n
              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#frontend","title":"Frontend","text":"

              To start the frontend in \"develop\" mode, you can execute the startup npm script within the folder frontend:

              cd frontend/\n# Install\nnpm i\n# Start\nDANGEROUSLY_DISABLE_HOST_CHECK=true npm start\n# See https://create-react-app.dev/docs/proxying-api-requests-in-development/#invalid-host-header-errors-after-configuring-proxy for why we use that flag in development mode\n

              Most of the time you would need to test the changes you made together with the backend. In that case, you would need to run the backend locally too:

              ./start prod up\n

              Note

              • Running prod would be faster because you would leverage the official images and you won't need to build the backend locally. In case you would need to test backend changes too at the same time, please use test and refer to the previous section of the documentation.
              • This works thanks to the directive proxy in the frontend/package.json configuration
              • It may happen that the backend build does not work due to incompatibility between the frontend version you are testing with the current complete IntelOwl version you are running. In those cases, considering that you don't need to build the frontend together with the backend because you are already testing it separately, we suggest to remove the first build step (the frontend part) from the main Dockerfile temporarily and build IntelOwl with only the backend. In this way there won't be conflict issues.
              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#certego-ui","title":"Certego-UI","text":"

              The IntelOwl Frontend is tightly linked to the certego-ui library. Most of the React components are imported from there. Because of this, it may happen that, during development, you would need to work on that library too. To install the certego-ui library, please take a look to npm link and remember to start certego-ui without installing peer dependencies (to avoid conflicts with IntelOwl dependencies):

              git clone https://github.com/certego/certego-ui.git\n# change directory to the folder where you have the cloned the library\ncd certego-ui/\n# install, without peer deps (to use packages of IntelOwl)\nnpm i --legacy-peer-deps\n# create link to the project (this will globally install this package)\nsudo npm link\n# compile the library\nnpm start\n

              Then, open another command line tab, create a link in the frontend to the certego-ui and re-install and re-start the frontend application (see previous section):

              cd frontend/\nnpm link @certego/certego-ui\n

              This trick will allow you to see reflected every changes you make in the certego-ui directly in the running frontend application.

              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#example-application","title":"Example application","text":"

              The certego-ui application comes with an example project that showcases the components that you can re-use and import to other projects, like IntelOwl:

              # To have the Example application working correctly, be sure to have installed `certego-ui` *without* the `--legacy-peer-deps` option and having it started in another command line\ncd certego-ui/\nnpm i\nnpm start\n# go to another tab\ncd certego-ui/example/\nnpm i\nnpm start\n
              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-plugin","title":"How to add a new Plugin","text":"

              IntelOwl was designed to ease the addition of new plugins. With a simple python script you can integrate your own engine or integrate an external service in a short time.

              There are two possible cases: 1. You are creating an entirely new Plugin, meaning that you actually wrote python code 2. You are creating a new Configuration for some code that already exists.

              If you are doing the step number 2, you can skip this paragraph.

              First, you need to create the python code that will be actually executed. You can easily take other plugins as example to write this. Then, you have to create a Python Module model. You can do this in the Django Admin page: You have to specify which type of Plugin you wrote, and its python module. Again, you can use as an example an already configured Python Module.

              Some Python Module requires to update some part of its code in a schedule way: for example Yara requires to update the rule repositories, QuarkEngine to update its database and so on. If the Python Module that you define need this type of behaviour, you have to configure two things: - In the python code, you have to override a method called update and put the updating logic (see other plugins for examples) there. - In the model class, you have to add the update_schedule (crontab syntax) that define when the update should be executed.

              Some Python Module requires further check to see if the service provider is able to answer requests; for example if you have done too many requests, or the website is currently down for maintenance and so on. If the Python Module that you define need this type of behaviour, you have to configure two things: - In the python code, you can override a method called health_check and put there the custom health check logic. As default, plugins will try to make an HTTP HEAD request to the configured url (the Plugin must have a url attribute). - In the model class, you have to add the health_check_schedule (crontab syntax) that define when the health check should be executed.

              Press Save and continue editing to, at the moment, manually ad the Parameters that the python code requires (the class attributes that you needed): 1. *name: Name of the parameter that will be dynamically added to the python class (if is a secret, in the python code a _ wil be prepended to the name) 2. *type: data type, string, list, dict, integer, boolean, float 3. *description 4. *required: true or false, meaning that a value is necessary to allow the run of the analyzer 5. *is_secret: true or false

              At this point, you can follow the specific guide for each plugin

              "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-analyzer","title":"How to add a new Analyzer","text":"

              You may want to look at a few existing examples to start to build a new one, such as:

              • shodan.py, if you are creating an observable analyzer
              • malpedia_scan.py, if you are creating a file analyzer
              • peframe.py, if you are creating a docker based analyzer
              • Please note: If the new analyzer that you are adding is free for the user to use, please add it in the FREE_TO_USE_ANALYZERS playbook. To do this you have to make a migration file; you can use 0026_add_mmdb_analyzer_free_to_use as a template.

              After having written the new python module, you have to remember to:

              1. Put the module in the file_analyzers or observable_analyzers directory based on what it can analyze
              2. Remember to use _monkeypatch() in its class to create automated tests for the new analyzer. This is a trick to have tests in the same class of its analyzer.
              3. Create the configuration inside django admin in Analyzers_manager/AnalyzerConfigs (* = mandatory, ~ = mandatory on conditions)
              4. *Name: specific name of the configuration
              5. *Python module: .
              6. *Description: description of the configuration
              7. *Routing key: celery queue that will be used
              8. *Soft_time_limit: maximum time for the task execution
              9. *Type: observable or file
              10. *Docker based: if the analyzer run through a docker instance
              11. *Maximum tlp: maximum tlp to allow the run on the connector
              12. ~Observable supported: required if type is observable
              13. ~Supported filetypes: required if type is file and not supported filetypes is empty
              14. Run hash: if the analyzer supports hash as inputs
              15. ~Run hash type: required if run hash is True
              16. ~Not supported filetypes: required if type is file and supported filetypes is empty
              17. "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#integrating-a-docker-based-analyzer","title":"Integrating a docker based analyzer","text":"

                If the analyzer you wish to integrate doesn't exist as a public API or python package, it should be integrated with its own docker image which can be queried from the main Django app.

                • It should follow the same design principle as the other such existing integrations, unless there's very good reason not to.
                • The dockerfile should be placed at ./integrations/<analyzer_name>/Dockerfile.
                • Two docker-compose files compose.yml for production and compose-tests.yml for testing should be placed under ./integrations/<analyzer_name>.
                • If your docker-image uses any environment variables, add them in the docker/env_file_integrations_template.
                • Rest of the steps remain same as given under \"How to add a new analyzer\".
                "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-connector","title":"How to add a new Connector","text":"

                You may want to look at a few existing examples to start to build a new one:

                • misp.py
                • opencti.py

                After having written the new python module, you have to remember to:

                1. Put the module in the connectors directory
                2. Remember to use _monkeypatch() in its class to create automated tests for the new connector. This is a trick to have tests in the same class of its connector.
                3. Create the configuration inside django admin in Connectors_manager/ConnectorConfigs (* = mandatory, ~ = mandatory on conditions)
                4. *Name: specific name of the configuration
                5. *Python module: .
                6. *Description: description of the configuration
                7. *Routing key: celery queue that will be used
                8. *Soft_time_limit: maximum time for the task execution
                9. *Maximum tlp: maximum tlp to allow the run on the connector
                10. *Run on failure: if the connector should be run even if the job fails
                11. "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-ingestor","title":"How to add a new Ingestor","text":"
                  1. Put the module in the ingestors directory
                  2. Remember to use _monkeypatch() in its class to create automated tests for the new ingestor. This is a trick to have tests in the same class of its ingestor.
                  3. Create the configuration inside django admin in Ingestors_manager/IngestorConfigs (* = mandatory, ~ = mandatory on conditions)
                  4. *Name: specific name of the configuration
                  5. *Python module: .
                  6. *Description: description of the configuration
                  7. *Routing key: celery queue that will be used
                  8. *Soft_time_limit: maximum time for the task execution
                  9. Playbook to Execute: Playbook that *will** be executed on every IOC retrieved
                  10. *Schedule: Crontab object that describes the schedule of the ingestor. You are able to create a new clicking the plus symbol.
                  11. "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-pivot","title":"How to add a new Pivot","text":"
                    1. Put the module in the pivots directory
                    2. Remember to use _monkeypatch() in its class to create automated tests for the new pivot. This is a trick to have tests in the same class of its pivot.
                    3. Create the configuration inside django admin in Pivots_manager/PivotConfigs (* = mandatory, ~ = mandatory on conditions)
                    4. *Name: specific name of the configuration
                    5. *Python module: .
                    6. *Description: description of the configuration
                    7. *Routing key: celery queue that will be used
                    8. *Soft_time_limit: maximum time for the task execution
                    9. Playbook to Execute: Playbook that *will** be executed in the Job generated by the Pivot
                    10. Most of the times you don't need to create a new Pivot Module. There are already some base modules that can be extended. The most important ones are the following 2: 1.AnyCompare: use this module if you want to create a custom Pivot from a specific value extracted from the results of the analyzers/connectors. How? you should populate the parameter field_to_compare with the dotted path to the field you would like to extract the value from. 2.SelfAnalyzable: use this module if you want to create a custom Pivot that would analyze again the same observable/file.

                      "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-visualizer","title":"How to add a new Visualizer","text":""},{"location":"Submodules/IntelOwl/docs/source/Contribute/#configuration","title":"Configuration","text":"
                      1. Put the module in the visualizers directory
                      2. Remember to use _monkeypatch() in its class to create automated tests for the new visualizer. This is a trick to have tests in the same class of its visualizer.
                      3. Create the configuration inside django admin in Visualizers_manager/VisualizerConfigs (* = mandatory, ~ = mandatory on conditions)
                      4. *Name: specific name of the configuration
                      5. *Python module: .
                      6. *Description: description of the configuration
                      7. *Config:
                        1. *Queue: celery queue that will be used
                        2. *Soft_time_limit: maximum time for the task execution
                      8. Playbook: Playbook that *must** have run to execute the visualizer
                      9. "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#python-class","title":"Python class","text":"

                        The visualizers' python code could be not immediate, so a small digression on how it works is necessary. Visualizers have as goal to create a data structure inside the Report that the frontend is able to parse and correctly visualize on the page. To do so, some utility classes have been made:

                        Class Description Visual representation/example VisualizablePage A single page of the final report, made of different levels. Each page added is represented as a new tab in frontend. VisualizableLevel Each level corresponds to a line in the final frontend visualizations. Every level is made of a VisualizableHorizontalList. The dimension of the level can be customized with the size parameter (1 is the biggest, 6 is the smallest). VisualizableHorizontalList An horizontal list of visualizable elements. In the example there is an horizontal list of vertical lists. VisualizableVerticalList A vertical list made of a name, a title, and the list of elements. VisualizableTable A table of visualizable elements. In the example there is a table of base and vertical lists. VisualizableBool The representation of a boolean value. It can be enabled or disabled with colors. VisualizableTitle The representation of a tuple, composed of a title and a value. VisualizableBase The representation of a base string. Can have a link attached to it and even an icon. The background color can be changed. The title above is composed by two `VisualizableBase`

                        Inside a Visualizer you can retrieve the reports of the analyzers and connectors that have been specified inside configuration of the Visualizer itself using .analyzer_reports() and .connector_reports(). At this point, you can compose these values as you wish wrapping them with the Visualizable classes mentioned before.

                        The best way to create a visualizer is to define several methods, one for each Visualizable you want to show in the UI, in your new visualizer and decore them with visualizable_error_handler_with_params. This decorator handles exceptions: in case there is a bug during the generation of a Visualizable element, it will be show an error instead of this component and all the other Visualizable are safe and will render correctly. Be careful using it because is a function returning a decorator! This means you need to use a syntax like this:

                        @visualizable_error_handler_with_params(error_name=\"custom visualizable\", error_size=VisualizableSize.S_2)\ndef custom_visualizable(self):\n   ...\n

                        instead of the syntax of other decorators that doesn't need the function call.

                        You may want to look at a few existing examples to start to build a new one:

                        • dns.py
                        • yara.py
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-share-your-plugin-with-the-community","title":"How to share your plugin with the community","text":"

                        To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration 1. You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/YOUR_PLUGIN_manager/migrations). The script will create the following models: 1. PythonModule 2. AnalyzerConfig 3. Parameter 4. PluginConfig

                        1. Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin AnalyzerConfig <new_analyzer_name>

                        Add the new analyzer in the lists in the docs: Usage. Also, if the analyzer provides additional optional configuration, add the available options here: Advanced-Usage

                        In the Pull Request remember to provide some real world examples (screenshots and raw JSON results) of some successful executions of the analyzer to let us understand how it would work.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-add-a-new-playbook","title":"How to add a new Playbook","text":"
                        1. Create the configuration inside django admin in Playbooks_manager/PlaybookConfigs (* = mandatory, ~ = mandatory on conditions)
                        2. *Name: specific name of the configuration
                        3. *Description: description of the configuration
                        4. *Type: list of types that are supported by the playbook
                        5. *Analyzers: List of analyzers that will be run
                        6. *Connectors: List of connectors that will be run
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-share-your-playbook-with-the-community","title":"How to share your playbook with the community","text":"

                        To allow other people to use your configuration, that is now stored in your local database, you have to export it and create a data migration You can use the django management command dumpplugin to automatically create the migration file for your new analyzer (you will find it under api_app/playbook_manager/migrations).

                        Example: docker exec -ti intelowl_uwsgi python3 manage.py dumpplugin PlaybookConfig <new_analyzer_name>

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-modify-a-plugin","title":"How to modify a plugin","text":"

                        If the changes that you have to make should stay local, you can just change the configuration inside the Django admin page.

                        But if, instead, you want your changes to be usable by every IntelOwl user, you have to create a new migration.

                        To do so, you can use the following snippets as an example: 1. You have to create a new migration file 2. Add as dependency the previous last migration of the package 3. You have to create a forward and a reverse function 4. You have to make the proper changes of the configuration inside these functions (change parameters, secrets, or even delete the configuration) 1. If changes are made, you have to validate the instance calling .full_clean() and then you can save the instance with .save()

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#example-how-to-add-a-new-parameter-in-the-configuration-with-a-default-value","title":"Example: how to add a new parameter in the configuration with a default value","text":"
                        def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   PluginConfig = apps.get_model(\"api_app\", \"PluginConfig\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   p = Parameter(name=\"mynewfield\", type=\"str\", description=\"Test field\", is_secret=False, required=True, python_module=pm)\n   p.full_clean()\n   p.save()\n   for connector in pm.connectorconfigs.all():\n    pc = PluginConfig(value=\"test\", connector_config=connector, python_module=pm, for_organization=False, owner=None, parameter=p)\n    pc.full_clean()\n    pc.save()\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#example-how-to-add-a-new-secret-in-the-configuration","title":"Example: how to add a new secret in the configuration","text":"
                        def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   p = Parameter(name=\"mynewsecret\", type=\"str\", description=\"Test field\", is_secret=True, required=True, python_module=pm)\n   p.full_clean()\n   p.save()\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#example-how-to-delete-a-parameter","title":"Example: how to delete a parameter","text":"
                        def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   Parameter.objects.get(name=\"myoldfield\", python_module=pm).delete()\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#example-how-to-change-the-default-value-of-a-parameter","title":"Example: how to change the default value of a parameter","text":"
                        def migrate(apps, schema_editor):\n   PythonModule = apps.get_model(\"api_app\", \"PythonModule\")\n   Parameter = apps.get_model(\"api_app\", \"Parameter\")\n   PluginConfig = apps.get_model(\"api_app\", \"PluginConfig\")\n   pm = PythonModule.objects.get(module=\"test.Test\", base_path=\"api_app.connectors_manager.connectors\")\n   p = Parameter.objects.get(name=\"myfield\", python_module=pm)\n   PluginConfig.objects.filter(parameter=p, python_module=pm, for_organization=False, owner=None ).update(value=\"newvalue\")\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#modifying-functionalities-of-the-certego-packages","title":"Modifying functionalities of the Certego packages","text":"

                        Since v4, IntelOwl leverages some packages from Certego:

                        • certego-saas that integrates some common reusable Django applications and tools that can be used for generic services.
                        • certego-ui that contains reusable React components for the UI.

                        If you need to modify the behavior or add feature to those packages, please follow the same rules for IntelOwl and request a Pull Request there. The same maintainers of IntelOwl will answer to you.

                        Follow these guides to understand how to start to contribute to them while developing for IntelOwl:

                        • certego-saas: create a fork, commit your changes in your local repo, then change the commit hash to the last one you made in the requirements file. Ultimately re-build the project
                        • certego-ui: Frontend doc
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-test-the-application","title":"How to test the application","text":"

                        IntelOwl makes use of the django testing framework and the unittest library for unit testing of the API endpoints and End-to-End testing of the analyzers and connectors.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#configuration_1","title":"Configuration","text":"
                        • In the encrypted folder tests/test_files.zip (password: \"intelowl\") there are some files that you can use for testing purposes.

                        • With the following environment variables you can customize your tests:

                        • DISABLE_LOGGING_TEST -> disable logging to get a clear output

                        • MOCK_CONNECTIONS -> mock connections to external API to test the analyzers without a real connection or a valid API key

                        • If you prefer to use custom inputs for tests, you can change the following environment variables in the environment file based on the data you would like to test:

                        • TEST_MD5
                        • TEST_URL
                        • TEST_IP
                        • TEST_DOMAIN
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#setup-containers","title":"Setup containers","text":"

                        The point here is to launch the code in your environment and not the last official image in Docker Hub. For this, use the test or the ci option when launching the containers with the ./start script.

                        • Use the test option to actually execute tests that simulate a real world environment without mocking connections.
                        • Use the ci option to execute tests in a CI environment where connections are mocked.
                        $ ./start test up\n$ # which corresponds to the command: docker-compose -f docker/default.yml -f docker/test.override.yml up\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#launch-tests","title":"Launch tests","text":"

                        Now that the containers are up, we can launch the test suite.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#backend_1","title":"Backend","text":""},{"location":"Submodules/IntelOwl/docs/source/Contribute/#run-all-tests","title":"Run all tests","text":"

                        Examples:

                        $ docker exec intelowl_uwsgi python3 manage.py test\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#run-tests-for-a-particular-plugin","title":"Run tests for a particular plugin","text":"

                        To test a plugin in real environment, i.e. without mocked data, we suggest that you use the GUI of IntelOwl directly. Meaning that you have your plugin configured, you have selected a correct observable/file to analyze, and the final report shown in the GUI of IntelOwl is exactly what you wanted.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#run-tests-available-in-a-particular-file","title":"Run tests available in a particular file","text":"

                        Examples:

                        $ docker exec intelowl_uwsgi python3 manage.py test tests.api_app tests.test_crons # dotted paths\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#frontend_1","title":"Frontend","text":"

                        All the frontend tests must be run from the folder frontend. The tests can contain log messages, you can suppress then with the environment variable SUPPRESS_JEST_LOG=True.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#run-all-tests_1","title":"Run all tests","text":"
                        npm test\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#run-a-specific-component-tests","title":"Run a specific component tests","text":"
                        npm test -- -t <componentPath>\n// example\nnpm test tests/components/auth/Login.test.jsx\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#run-a-specific-test","title":"Run a specific test","text":"
                        npm test -- -t '<describeString> <testString>'\n// example\nnpm test -- -t \"Login component User login\"\n
                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#create-a-pull-request","title":"Create a pull request","text":""},{"location":"Submodules/IntelOwl/docs/source/Contribute/#remember","title":"Remember!!!","text":"

                        Please create pull requests only for the branch develop. That code will be pushed to master only on a new release.

                        Also remember to pull the most recent changes available in the develop branch before submitting your PR. If your PR has merge conflicts caused by this behavior, it won't be accepted.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#install-testing-requirements","title":"Install testing requirements","text":"

                        Run pip install -r requirements/test-requirements.txt to install the requirements to validate your code.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#pass-linting-and-tests","title":"Pass linting and tests","text":"
                        1. Run psf/black to lint the files automatically, then flake8 to check and isort:

                        (if you installed pre-commit this is performed automatically at every commit)

                        $ black . --exclude \"migrations|venv\"\n$ flake8 . --show-source --statistics\n$ isort . --profile black --filter-files --skip venv\n

                        if flake8 shows any errors, fix them.

                        1. Run the build and start the app using the docker-compose test file. In this way, you would launch the code in your environment and not the last official image in Docker Hub:
                        $ ./start ci build\n$ ./start ci up\n
                        1. Here, we simulate the GitHub CI tests locally by running the following 3 tests:
                        $ docker exec -ti intelowl_uwsgi unzip -P intelowl tests/test_files.zip -d test_files\n$ docker exec -ti intelowl_uwsgi python manage.py test tests\n

                        Note: IntelOwl has dynamic testing suite. This means that no explicit analyzers/connector tests are required after the addition of a new analyzer or connector.

                        If everything is working, before submitting your pull request, please squash your commits into a single one!

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#how-to-squash-commits-to-a-single-one","title":"How to squash commits to a single one","text":"
                        • Run git rebase -i HEAD~[NUMBER OF COMMITS]
                        • You should see a list of commits, each commit starting with the word \"pick\".
                        • Make sure the first commit says \"pick\" and change the rest from \"pick\" to \"squash\". -- This will squash each commit into the previous commit, which will continue until every commit is squashed into the first commit.
                        • Save and close the editor.
                        • It will give you the opportunity to change the commit message.
                        • Save and close the editor again.
                        • Then you have to force push the final, squashed commit: git push --force-with-lease origin.

                        Squashing commits can be a tricky process but once you figure it out, it's really helpful and keeps our repo concise and clean.

                        "},{"location":"Submodules/IntelOwl/docs/source/Contribute/#debug-application-problems","title":"Debug application problems","text":"

                        Keep in mind that, if any errors arise during development, you would need to check the application logs to better understand what is happening so you can easily address the problem.

                        This is the reason why it is important to add tons of logs in the application...if they are not available in time of needs you would cry really a lot.

                        Where are IntelOwl logs? With a default installation of IntelOwl, you would be able to get the application data from the following paths in your OS: * /var/lib/docker/volumes/intel_owl_generic_logs/_data/django: Django Application logs * /var/lib/docker/volumes/intel_owl_generic_logs/_data/uwsgi: Uwsgi application server logs * /var/lib/docker/volumes/intel_owl_nginx_logs/_data/: Nginx Web Server Logs

                        "},{"location":"Submodules/IntelOwl/docs/source/Installation/","title":"Installation","text":""},{"location":"Submodules/IntelOwl/docs/source/Installation/#requirements","title":"Requirements","text":"

                        The project leverages docker compose with a custom Bash script and you need to have the following packages installed in your machine: * docker - v19.03.0+ * docker-compose - v2.3.4+

                        In some systems you could find pre-installed older versions. Please check this and install a supported version before attempting the installation. Otherwise it would fail. Note: We've added a new Bash script initialize.sh that will check compatibility with your system and attempt to install the required dependencies.

                        Note

                        • The project uses public docker images that are available on Docker Hub
                        • IntelOwl is tested and supported to work in a Debian distro. More precisely we suggest using Ubuntu. Other Linux-based OS should work but that has not been tested much. It may also run on Windows, but that is not officially supported.
                        • IntelOwl does not support ARM at the moment. We'll fix this with the next v6.0.5 release
                        • Before installing remember that you must comply with the LICENSE and the Legal Terms
                        • Warning

                          The start script requires a `bash` version > 4 to run. Note that macOS is shipped with an older version of bash. Please ensure to upgrade before running the script."},{"location":"Submodules/IntelOwl/docs/source/Installation/#tldr","title":"TL;DR","text":"

                          Obviously we strongly suggest reading through all the page to configure IntelOwl in the most appropriate way.

                          However, if you feel lazy, you could just install and test IntelOwl with the following steps. docker will be run with sudo if permissions/roles have not been set.

                          # clone the IntelOwl project repository\ngit clone https://github.com/intelowlproject/IntelOwl\ncd IntelOwl/\n\n# run helper script to verify installed dependencies and configure basic stuff\n./initialize.sh\n\n# start the app\n./start prod up\n# now the application is running on http://localhost:80\n\n# create a super user \nsudo docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser\n\n# now you can login with the created user from http://localhost:80/login\n\n# Have fun!\n

                          Warning

                          The first time you start IntelOwl, a lot of database migrations are being applied. This requires some time. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page."},{"location":"Submodules/IntelOwl/docs/source/Installation/#deployment-components","title":"Deployment Components","text":"

                          IntelOwl is composed of various different technologies, namely: * React: Frontend, using CRA and certego-ui * Django: Backend * PostgreSQL: Database * Redis: Message Broker * Celery: Task Queue * Nginx: Reverse proxy for the Django API and web asssets. * Uwsgi: Application Server * Daphne: Asgi Server for WebSockets * Elastic Search (optional): Auto-sync indexing of analysis' results. * Kibana (optional): GUI for Elastic Search. We provide a saved configuration with dashboards and visualizations. * Flower (optional): Celery Management Web Interface

                          All these components are managed via docker compose.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#deployment-preparation","title":"Deployment Preparation","text":"
                          • Environment configuration (required)
                          • Database configuration (required)
                          • Web server configuration (optional)
                          • Analyzers configuration (optional)

                          Open a terminal and execute below commands to construct new environment files from provided templates.

                          ./initialize.sh\n
                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#environment-configuration-required","title":"Environment configuration (required)","text":"

                          In the docker/env_file_app, configure different variables as explained below.

                          REQUIRED variables to run the image: * DB_HOST, DB_PORT, DB_USER, DB_PASSWORD: PostgreSQL configuration (The DB credentals should match the ones in the env_file_postgres). If you like, you can configure the connection to an external PostgreSQL instance in the same variables. Then, to avoid to run PostgreSQL locally, please run IntelOwl with the option --use-external-database. Otherwise, DB_HOST must be postgres to have the app properly communicate with the PostgreSQL container. * DJANGO_SECRET: random 50 chars key, must be unique. If you do not provide one, Intel Owl will automatically set a secret key and use the same for each run. The key is generated by initialize.sh script.

                          Strongly recommended variable to set: * INTELOWL_WEB_CLIENT_DOMAIN (example: localhost/mywebsite.com): the web domain of your instance, this is used for generating links to analysis results.

                          Optional configuration: * OLD_JOBS_RETENTION_DAYS: Database retention for analysis results (default: 14 days). Change this if you want to keep your old analysis longer in the database.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#other-optional-configuration-to-enable-specific-services-features","title":"Other optional configuration to enable specific services / features","text":"

                          Configuration required to enable integration with Slack: * SLACK_TOKEN: Slack token of your Slack application that will be used to send/receive notifications * DEFAULT_SLACK_CHANNEL: ID of the Slack channel you want to post the message to

                          Configuration required to enable Re-Captcha in the Login and the Registration Page: In the docker/env_file_app: * USE_RECAPTCHA: if you want to use recaptcha on your login * RECAPTCHA_SECRET_KEY: your recaptcha secret key In the frontend/public/env.js: * RECAPTCHA_SITEKEY: Recaptcha Key for your site

                          Configuration required to have InteOwl sending Emails (registration requests, mail verification, password reset/change, etc) * DEFAULT_FROM_EMAIL: email address used for automated correspondence from the site manager (example: noreply@mydomain.com) * DEFAULT_EMAIL: email address used for correspondence with users (example: info@mydomain.com) * EMAIL_HOST: the host to use for sending email with SMTP * EMAIL_HOST_USER: username to use for the SMTP server defined in EMAIL_HOST * EMAIL_HOST_PASSWORD: password to use for the SMTP server defined in EMAIL_HOST. This setting is used in conjunction with EMAIL_HOST_USER when authenticating to the SMTP server. * EMAIL_PORT: port to use for the SMTP server defined in EMAIL_HOST. * EMAIL_USE_TLS: whether to use an explicit TLS (secure) connection when talking to the SMTP server, generally used on port 587. * EMAIL_USE_SSL: whether to use an implicit TLS (secure) connection when talking to the SMTP server, generally used on port 465.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#database-configuration-required-if-running-postgresql-locally","title":"Database configuration (required if running PostgreSQL locally)","text":"

                          If you use a local PostgreSQL instance (this is the default), in the env_file_postgres you have to configure different variables as explained below.

                          Required variables: * POSTGRES_PASSWORD (same as DB_PASSWORD) * POSTGRES_USER (same as DB_USER) * POSTGRES_DB (default: intel_owl_db)

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#logrotate-configuration-strongly-recommended","title":"Logrotate configuration (strongly recommended)","text":"

                          If you want to have your logs rotated correctly, we suggest you to add the configuration for the system Logrotate. To do that you can leverage the initialize.sh script. Otherwise, if you have skipped that part, you can manually install logrotate by launching the following script:

                          cd ./docker/scripts\n./install_logrotate.sh\n

                          We decided to do not leverage Django Rotation Configuration because it caused problematic concurrency issues, leading to logs that are not rotated correctly and to apps that do not log anymore. Logrotate configuration is more stable.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#crontab-configuration-recommended-for-advanced-deployments","title":"Crontab configuration (recommended for advanced deployments)","text":"

                          We added few Crontab configurations that could be installed in the host machine at system level to solve some possible edge-case issues: * Memory leaks: Once a week it is suggested to do a full restart of the application to clean-up the memory used by the application. Practical experience suggest us to do that to solve some recurrent memory issues in Celery. A cron called application_restart has been added for this purpose (it uses the absolute path of start script in the container). This cron assumes that you have executed IntelOwl with the parameters --all_analyzers. If you didn't, feel free to change the cron as you wish.

                          This configuration is optional but strongly recommended for people who want to have a production grade deployment. To install it you need to run the following script in each deployed server:

                          cd ./docker/scripts\n./install_crontab.sh\n

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#web-server-configuration-required-for-enabling-https","title":"Web server configuration (required for enabling HTTPS)","text":"

                          Intel Owl provides basic configuration for: * Nginx (configuration/nginx/http.conf) * Uwsgi (configuration/intel_owl.ini)

                          In case you enable HTTPS, remember to set the environment variable HTTPS_ENABLED as \"enabled\" to increment the security of the application.

                          There are 3 options to execute the web server:

                          • HTTP only (default)

                            The project would use the default deployment configuration and HTTP only.

                          • HTTPS with your own certificate

                            The project provides a template file to configure Nginx to serve HTTPS: configuration/nginx/https.conf.

                            You should change ssl_certificate, ssl_certificate_key and server_name in that file and put those required files in the specified locations.

                            Then you should call the ./start script with the parameter --https to leverage the right Docker Compose file for HTTPS.

                            Plus, if you use Flower, you should change in the docker/flower.override.yml the flower_http.conf with flower_https.conf.

                          • HTTPS with Let's Encrypt

                            We provide a specific docker-compose file that leverages Traefik to allow fast deployments of public-faced and HTTPS-enabled applications.

                            Before using it, you should configure the configuration file docker/traefik.override.yml by changing the email address and the hostname where the application is served. For a detailed explanation follow the official documentation: Traefix doc.

                            After the configuration is done, you can add the option --traefik while executing ./start

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#run","title":"Run","text":"

                          Important Info

                          IntelOwl depends heavily on docker and docker compose so as to hide this complexity from the enduser the project leverages a custom shell script (start) to interface with docker compose. You may invoke $ ./start --help to get help and usage info. The CLI provides the primitives to correctly build, run or stop the containers for IntelOwl. Therefore,
                          • It is possible to attach every optional docker container that IntelOwl has: multi_queue with traefik enabled while every optional docker analyzer is active.
                          • It is possible to insert an optional docker argument that the CLI will pass to docker-compose

                          Now that you have completed different configurations, starting the containers is as simple as invoking:

                          $ ./start prod up\n

                          You can add the docker options -d to run the application in the background.

                          Important Info

                          All docker and docker compose specific options must be passed at the end of the script, after a -- token. This token indicates the end of IntelOwl's options and the beginning of Docker options. Example:
                          ./start prod up -- -d\n

                          Hint

                          Starting from IntelOwl 4.0.0, with the startup script you can select which version of IntelOwl you want to run (--version). This can be helpful to keep using old versions in case of retrocompatibility issues. The --version parameter checks out the Git Repository to the Tag of the version that you have chosen. This means that if you checkout to a v3.x.x version, you won't have the --version parameter anymore so you would need to manually checkout back to the master branch to use newer versions."},{"location":"Submodules/IntelOwl/docs/source/Installation/#stop","title":"Stop","text":"

                          To stop the application you have to: * if executed without -d parameter: press CTRL+C * if executed with -d parameter: ./start prod down

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#cleanup-of-database-and-application","title":"Cleanup of database and application","text":"

                          This is a destructive operation but can be useful to start again the project from scratch

                          ./start prod down -- -v

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#after-deployment","title":"After Deployment","text":""},{"location":"Submodules/IntelOwl/docs/source/Installation/#users-creation","title":"Users creation","text":"

                          You may want to run docker exec -ti intelowl_uwsgi python3 manage.py createsuperuser after first run to create a superuser. Then you can add other users directly from the Django Admin Interface after having logged with the superuser account. To manage users, organizations and their visibility please refer to this section

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#update-and-rebuild","title":"Update and Rebuild","text":""},{"location":"Submodules/IntelOwl/docs/source/Installation/#rebuilding-the-project-creating-custom-docker-build","title":"Rebuilding the project / Creating custom docker build","text":"

                          If you make some code changes and you like to rebuild the project, follow these steps:

                          1. ./start test build -- --tag=<your_tag> . to build the new docker image.
                          2. Add this new image tag in the docker/test.override.yml file.
                          3. Start the containers with ./start test up -- --build.
                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#update-to-the-most-recent-version","title":"Update to the most recent version","text":"

                          To update the project with the most recent available code you have to follow these steps:

                          $ cd <your_intel_owl_directory> # go into the project directory\n$ git pull # pull new changes\n$ ./start prod down # kill and destroy the currently running IntelOwl containers \n$ ./start prod up # restart the IntelOwl application\n

                          Note

                          After an upgrade, sometimes a database error in Celery Containers could happen. That could be related to new DB migrations which are not applied by the main Uwsgi Container yet. Do not worry. Wait few seconds for the Uwsgi container to start correctly, then put down the application again and restart it. The problem should be solved. If not, please feel free to open an issue on Github

                          Note

                          After having upgraded IntelOwl, in case the application does not start and you get an error like this:
                          PermissionError: [Errno 13] Permission denied: '/var/log/intel_owl/django/authentication.log\n
                          just run this:
                          sudo chown -R www-data:www-data /var/lib/docker/volumes/intel_owl_generic_logs/_data/django\n
                          and restart IntelOwl. It should solve the permissions problem.

                          Warning

                          Major versions of IntelOwl are usually incompatible from one another. Maintainers strive to keep the upgrade between major version easy but it's not always like that. Below you can find the additional process required to upgrade from each major versions."},{"location":"Submodules/IntelOwl/docs/source/Installation/#updating-to-600-from-a-5xx-version","title":"Updating to >=6.0.0 from a 5.x.x version","text":"

                          IntelOwl v6 introduced some major changes regarding how the project is started. Before upgrading, some important things should be checked by the administrator: * Docker Compose V1 support has been dropped project-wide. If you are still using a Compose version prior to v2.3.4, please upgrade to a newer version or install Docker Compose V2. * IntelOwl is now started with the new Bash start script that has the same options as the old Python start.py script but is more manageable and has decreased the overall project dependencies. The start.py script has now been removed. Please use the new start script instead. * The default message broker is now Redis. We have replaced Rabbit-MQ for Redis to allow support for Websockets in the application: * This change is transparent if you use our start script to run IntelOwl. That would spawn a Redis instance instead of a Rabbit-MQ one locally. * If you were using an external broker like AWS SQS or a managed Rabbit-MQ, they are still supported but we suggest to move to a Redis supported service to simplify the architecture (because Redis is now mandatory for Websockets) * Support for multiple jobs with multiple playbooks has been removed. Every Observable or File in the request will be processed by a single playbook. * We upgraded the base PostgreSQL image from version 12 to version 16. You have 2 choice: * remove your actual database and start from scratch with a new one * maintain your database and do not update Postgres. This could break the application at anytime because we do not support it anymore. * if you want to keep your old DB, follow the migration procedure you can find below

                          Warning

                          CARE! We are providing this database migration procedure to help the users to migrate to a new PostgreSQL version. Upgrading PostgreSQL is outside the scope of the IntelOwl project so we do not guarantee that everything will work as intended. In case of doubt, please check the official PostgreSQL documentation. Upgrade at your own risk.

                          The database migration procedure is as follows: - You have IntelOwl version 5.x.x up and running - Bring down the application (you can use the start script or manually concatenate your docker compose configuration ) - Go inside the docker folder cd docker - Bring only the postgres 12 container up docker run -d --name intelowl_postgres_12 -v intel_owl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:12-alpine - Dump the entire database. You need the user and the database that you configured during startup for this docker exec -t intelowl_postgres_12 pg_dump -U <POSTGRES_USER> -d <POSTGRES_DB> --no-owner > /tmp/dump_intelowl.sql - Stop che container docker container stop intelowl_postgres_12 - Remove the backup container docker container rm intelowl_postgres_12 - Remove the postgres volume docker volume rm intel_owl_postgres_data <------------- remove old data, this is not exactly necessary because the new postgres has a different volume name - Start the intermediary postgres 16 container docker run -d --name intelowl_postgres_16 -v intelowl_postgres_data:/var/lib/postgresql/data/ --env-file env_file_postgres library/postgres:16-alpine - Add the data to the volume cat /tmp/dump_intelowl.sql | docker exec -i intelowl_postgres_16 psql -U <POSTGRES_USER> -d <POSTGRES_DB> - Stop the intermediary container docker container stop intelowl_postgres_16 - Remove the intermediary container docker container rm intelowl_postgres_16 - Update IntelOwl to the latest version - Bring up the application back again (you can use the start script or manually concatenate your docker compose configuration)

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#updating-to-500-from-a-4xx-version","title":"Updating to >=5.0.0 from a 4.x.x version","text":"

                          IntelOwl v5 introduced some major changes regarding how the plugins and their related configuration are managed in the application. Before upgrading, some important things should be checked by the administrator: * A lot of database migrations will need to be applied. Just be patient few minutes once you install the new major release. If you get 500 status code errors in the GUI, just wait few minutes and then refresh the page. * We moved away from the old big analyzer_config.json which was storing all the base configuration of the Analyzers to a database model (we did the same for all the other plugins types too). This allows us to manage plugins creation/modification/deletion in a more reliable manner and via the Django Admin Interface. If you have created custom plugins and changed those <plugins>_config.json file manually, you would need to re-create those custom plugins again from the Django Admin Interface. To do that please follow the related new documentation * We have REMOVED all the analyzers that we deprecated during the v4 releases cycle. Please substitute them with their respective new names, in case they have a replacement. * REMOVED Pulsedive_Active_IOC analyzer. Please substitute it with the new Pulsedive analyzer. * REMOVED Fortiguard analyzer because endpoint does not work anymore. No substitute. * REMOVED Rendertron analyzer not working as intended. No substitute. * REMOVED ThreatMiner, SecurityTrails and Robtex various analyzers and substituted with new versions. * REMOVED Doc_Info_Experimental. Its functionality (XLM Macro parsing) is moved to Doc_Info * REMOVED Strings_Info_Classic. Please use Strings_Info * REMOVED Strings_Info_ML. Please use Strings_Info and set the parameter rank_strings to True * REMOVED all Yara_Scan_<repo> analyzers. They all went merged in the single Yara analyzer * REMOVED Darksearch_Query analyzer because the service does not exist anymore. No substitute. * REMOVED UnpacMe_EXE_Unpacker. Please use UnpacMe * REMOVED BoxJS_Scan_JavaScript. Please use BoxJS * REMOVED all Anomali_Threatstream_<option> analyzers. Now we have a single Anomali_Threatstream analyzer. Use the parameters to select the specific API you need.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#updating-to-500-from-a-3xx-version","title":"Updating to >=5.0.0 from a 3.x.x version","text":"

                          This is not supported. Please perform a major upgrade once at a time.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#updating-to-400-from-a-3xx-version","title":"Updating to >=4.0.0 from a 3.x.x version","text":"

                          IntelOwl v4 introduced some major changes regarding the permission management, allowing an easier way to manage users and visibility. But that did break the previous available DB. So, to migrate to the new major version you would need to delete your DB. To do that, you would need to delete your volumes and start the application from scratch.

                          python3 start.py prod down -v\n
                          Please be aware that, while this can be an important effort to manage, the v4 IntelOwl provides an easier way to add, invite and manage users from the application itself. See the Organization section.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#updating-to-200-from-a-1xx-version","title":"Updating to >=2.0.0 from a 1.x.x version","text":"

                          Users upgrading from previous versions need to manually move env_file_app, env_file_postgres and env_file_integrations files under the new docker directory.

                          "},{"location":"Submodules/IntelOwl/docs/source/Installation/#updating-to-v13x-from-any-prior-version","title":"Updating to >v1.3.x from any prior version","text":"

                          If you are updating to >v1.3.0 from any prior version, you need to execute a helper script so that the old data present in the database doesn't break.

                          1. Follow the above updation steps, once the docker containers are up and running execute the following in a new terminal

                            docker exec -ti intelowl_uwsgi bash\n

                            to get a shell session inside the IntelOwl's container.

                          2. Now just copy and paste the below command into this new session,

                            curl https://gist.githubusercontent.com/Eshaan7/b111f887fa8b860c762aa38d99ec5482/raw/758517acf87f9b45bd22f06aee57251b1f3b1bbf/update_to_v1.3.0.py | python -\n
                          3. If you see \"Update successful!\", everything went fine and now you can enjoy the new features!

                          "},{"location":"Submodules/IntelOwl/docs/source/Introduction/","title":"Introduction","text":"

                          IntelOwl was designed with the intent to help the community, in particular those researchers that can not afford commercial solutions, in the generation of threat intelligence data, in a simple, scalable and reliable way.

                          Main features:

                          • Provides enrichment of Threat Intel for malware as well as observables (IP, Domain, URL, hash, etc).
                          • This application is built to scale out and to speed up the retrieval of threat info.
                          • Thanks to the official libraries pyintelowl and go-intelowl, it can be integrated easily in your stack of security tools to automate common jobs usually performed, for instance, by SOC analysts manually.
                          • Intel Owl is composed of:
                          • analyzers that can be run to either retrieve data from external sources (like VirusTotal or AbuseIPDB) or to generate intel from internally available tools (like Yara or Oletools)
                          • connectors that can be run to export data to external platforms (like MISP or OpenCTI)
                          • visualizers that can be run to create custom visualizations of analyzers results
                          • playbooks that are meant to make analysis easily repeatable
                          • API REST written in Django and Python 3.9.
                          • Built-in frontend client written in ReactJS, with certego-ui: provides features such as dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, etc.
                          "},{"location":"Submodules/IntelOwl/docs/source/Introduction/#publications-and-media","title":"Publications and media","text":"

                          To know more about the project and its growth over time, you may be interested in reading the following official blog posts and/or videos:

                          • The Honeynet Workshop: Denmark 2024
                          • Certego Blog: v6 Announcement (in Italian)
                          • HackinBo 2023 Presentation (in Italian)
                          • Certego Blog: v.5.0.0 Announcement
                          • Youtube demo: IntelOwl v4
                          • Certego Blog: v.4.0.0 Announcement
                          • Honeynet Blog: v3.0.0 Announcement
                          • Intel Owl on Daily Swig
                          • Honeynet Blog: v1.0.0 Announcement
                          • Certego Blog: First announcement

                          Feel free to ask everything it comes to your mind about the project to the author: Matteo Lodi (Twitter).

                          We also have a dedicated twitter account for the project: @intel_owl.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/","title":"Usage","text":"

                          This page includes the most important things to know and understand when using IntelOwl.

                          • How to interact with IntelOwl
                          • Plugins Framework
                          • Analyzers
                          • Connectors
                          • Pivots
                          • Visualizers
                          • Ingestors
                          • Playbooks
                          • Generic Plugin Creation, Configuration and Customization
                          • Enabling or Disabling Plugins
                          • Special Plugins operations
                          • TLP Support
                          • Investigations Framework
                          • Create and populate an Investigation
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#how-to-interact-with-intelowl","title":"How to interact with IntelOwl","text":"

                          Intel Owl main objective is to provide a single API interface to query in order to retrieve threat intelligence at scale.

                          There are multiple ways to interact with the Intel Owl APIs,

                          1. Web Interface

                          2. Built-in Web interface with dashboard, visualizations of analysis data, easy to use forms for requesting new analysis, tags management and more features

                          3. pyIntelOwl (CLI/SDK)

                          4. Official Python client that is available at: PyIntelOwl,

                          5. Can be used as a library for your own python projects or...
                          6. directly via the command line interface.

                          7. goIntelOwl (CLI/SDK)

                          8. Official GO client that is available at: go-intelowl

                          Hint: Tokens Creation

                          The server authentication is managed by API tokens. So, if you want to interact with Intel Owl, you have two ways to do that:
                          • If you are a normal user, you can go to the \"API Access/Sessions\" section of the GUI and create a Token there.
                          • If you are an administrator of IntelOwl, you can create one or more unprivileged users from the Django Admin Interface and then generate a token for those users.
                          Afterwards you can leverage the created tokens with the Intel Owl Client."},{"location":"Submodules/IntelOwl/docs/source/Usage/#plugins-framework","title":"Plugins Framework","text":"

                          Plugins are the core modular components of IntelOwl that can be easily added, changed and customized. There are several types of plugins:

                          • Analyzers
                          • Connectors
                          • Pivots
                          • Visualizers
                          • Ingestors
                          • Playbooks
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#analyzers","title":"Analyzers","text":"

                          Analyzers are the most important plugins in IntelOwl. They allow to perform data extraction on the observables and/or files that you would like to analyze.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#analyzers-list","title":"Analyzers list","text":"

                          The following is the list of the available analyzers you can run out-of-the-box. You can also navigate the same list via the

                          • Graphical Interface: once your application is up and running, go to the \"Plugins\" section
                          • pyintelowl: $ pyintelowl get-analyzer-config
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#file-analyzers","title":"File analyzers:","text":""},{"location":"Submodules/IntelOwl/docs/source/Usage/#internal-tools","title":"Internal tools","text":"
                          • APKiD: APKiD identifies many compilers, packers, obfuscators, and other weird stuff from an APK or DEX file.
                          • BoxJS_Scan_Javascript: Box-JS is a tool for studying JavaScript malware.
                          • Capa_Info: Capa detects capabilities in executable files
                          • Capa_Info_Shellcode: Capa detects capabilities in shellcode
                          • ClamAV: scan a file via the ClamAV AntiVirus Engine. IntelOwl automatically keep ClamAV updated with official and unofficial open source signatures
                          • Doc_Info: static document analysis with new features to analyze XLM macros, encrypted macros and more (combination of Oletools and XLMMacroDeobfuscator)
                          • ELF_Info: static ELF analysis with pyelftools and telfhash
                          • File_Info: static generic File analysis (hashes, magic and exiftool)
                          • Floss: Mandiant Floss Obfuscated String Solver in files
                          • Hfinger: create fingerprints of malware HTTPS requests using Hfinger
                          • PE_Info: static PE analysis with pefile
                          • PEframe_Scan: Perform static analysis on Portable Executable malware and malicious MS Office documents with PeFrame
                          • Permhash: create hash of manifest permssions found in APK, Android manifest, Chrome extensions or Chrome extension manifest using Permhash
                          • PDF_Info: static PDF analysis (peepdf + pdfid)
                          • Qiling_Linux: Qiling qiling linux binary emulation.
                          • Qiling_Linux_Shellcode: Qiling qiling linux shellcode emulation.
                          • Qiling_Windows: Qiling qiling windows binary emulation.
                          • Qiling_Windows_Shellcode: Qiling qiling windows shellcode emulation.
                          • Quark_Engine: Quark Engine is an Obfuscation-Neglect Android Malware Scoring System.
                          • Rtf_Info: static RTF analysis (Oletools)
                          • Signature_Info: PE signature extractor with osslsigncode
                          • Speakeasy: Mandiant Speakeasy binary emulation
                          • SpeakEasy_Shellcode: Mandiant Speakeasy shellcode emulation
                          • Strings_Info: Strings extraction. Leverages Mandiant's Stringsifter
                          • Suricata: Analyze PCAPs with open IDS signatures with Suricata engine
                          • Thug_HTML_Info: Perform hybrid dynamic/static analysis on a HTML file using Thug low-interaction honeyclient
                          • Xlm_Macro_Deobfuscator: XlmMacroDeobfuscator deobfuscate xlm macros
                          • Yara: scan a file with
                          • ATM malware yara rules
                          • bartblaze yara rules
                          • community yara rules
                          • StrangerealIntel
                          • Neo23x0 yara rules
                          • Intezer yara rules
                          • Inquest yara rules
                          • McAfee yara rules
                          • Samir Threat Hunting yara rules
                          • Stratosphere yara rules
                          • Mandiant yara rules
                          • ReversingLabs yara rules
                          • YARAify rules
                          • SIFalcon rules
                          • Elastic rules
                          • JPCERTCC Yara rules
                          • HuntressLab Yara rules
                          • elceef Yara Rules
                          • dr4k0nia Yara rules
                          • Facebook Yara rules
                          • edelucia Yara rules
                          • LOLDrivers Yara Rules
                          • your own added signatures. See Advanced-Usage for more details.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#external-services","title":"External services","text":"
                          • CapeSandbox: CAPESandbox automatically scans suspicious files using the CapeSandbox API. Analyzer works for private instances as well.
                          • Cymru_Hash_Registry_Get_File: Check if a particular file is known to be malware by Team Cymru
                          • Cuckoo_Scan: scan a file on Cuckoo (this analyzer is disabled by default. You have to change that flag in the config to use it)
                          • DocGuard_Upload_File: Analyze office files in seconds. DocGuard.
                          • Dragonfly_Emulation: Emulate malware against Dragonfly sandbox by Certego S.R.L.
                          • FileScan_Upload_File: Upload your file to extract IoCs from executable files, documents and scripts via FileScan.io API.
                          • HashLookupServer_Get_File: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
                          • HybridAnalysis_Get_File: check file hash on HybridAnalysis sandbox reports
                          • Intezer_Scan: scan a file on Intezer. Register for a free community account here. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
                          • Malpedia_Scan: scan a binary or a zip file (pwd:infected) against all the yara rules available in Malpedia
                          • MalwareBazaar_Get_File: Check if a particular malware sample is known to MalwareBazaar
                          • MISPFIRST_Check_Hash: check a file hash on the FIRST MISP instance
                          • MISP_Check_Hash: check a file hash on a MISP instance
                          • MWDB_Scan: mwdblib Retrieve malware file analysis from repository maintained by CERT Polska MWDB. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
                          • OTX_Check_Hash: check file hash on Alienvault OTX
                          • SublimeSecurity: Analyze an Email with Sublime Security live flow
                          • Triage_Scan: leverage Triage sandbox environment to scan various files
                          • UnpacMe: UnpacMe is an automated malware unpacking service
                          • Virushee_Scan: Check file hash on Virushee API. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
                          • VirusTotal_v3_File: check the file hash on VirusTotal. With TLP CLEAR, in case the hash is not found, you would send the file to the service.
                          • YARAify_File_Scan: scan a file against public and non-public YARA and ClamAV signatures in YARAify public service
                          • YARAify_File_Search: scan an hash against YARAify database
                          • Zippy_scan : Zippy: Fast method to classify text as AI or human-generated; takes in lzma,zlib,brotli as input based engines; ensemble being default.
                          • Blint: Blint is a Binary Linter that checks the security properties and capabilities of your executables. Supported binary formats: - Android (apk, aab) - ELF (GNU, musl) - PE (exe, dll) - Mach-O (x64, arm64)
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#observable-analyzers-ip-domain-url-hash","title":"Observable analyzers (ip, domain, url, hash)","text":""},{"location":"Submodules/IntelOwl/docs/source/Usage/#internal-tools_1","title":"Internal tools","text":"
                          • CheckDMARC: An SPF and DMARC DNS records validator for domains.
                          • DNStwist: Scan a url/domain to find potentially malicious permutations via dns fuzzing. dnstwist repo
                          • Thug_URL_Info: Perform hybrid dynamic/static analysis on a URL using Thug low-interaction honeyclient
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#external-services_1","title":"External services","text":"
                          • AbuseIPDB: check if an ip was reported on AbuseIPDB
                          • Abusix: get abuse contacts of an IP address from Abusix
                          • BGP Ranking: BGP-Ranking provides a way to collect such malicious activities, aggregate the information per ASN and provide a ranking model to rank the ASN from the most malicious to the less malicious ASN.
                          • Anomali_Threatstream_PassiveDNS: Return information from passive dns of Anomali. On Anomali Threatstream PassiveDNS Api.
                          • Auth0: scan an IP against the Auth0 API
                          • BinaryEdge: Details about an Host. List of recent events for the specified host, including details of exposed ports and services using IP query and return list of subdomains known from the target domains using domain query
                          • BitcoinAbuse : Check a BTC address against bitcoinabuse.com, a public database of BTC addresses used by hackers and criminals.
                          • Censys_Search: scan an IP address against Censys View API
                          • CheckPhish: CheckPhish can detect phishing and fraudulent sites.
                          • CIRCLPassiveDNS: scan an observable against the CIRCL Passive DNS DB
                          • CIRCLPassiveSSL: scan an observable against the CIRCL Passive SSL DB
                          • Classic_DNS: Retrieve current domain resolution with default DNS
                          • CloudFlare_DNS: Retrieve current domain resolution with CloudFlare DoH (DNS over HTTPS)
                          • CloudFlare_Malicious_Detector: Leverages CloudFlare DoH to check if a domain is related to malware
                          • Crowdsec: check if an IP was reported on Crowdsec Smoke Dataset
                          • Cymru_Hash_Registry_Get_Observable: Check if a particular hash is available in the malware hash registry of Team Cymru
                          • DNSDB: scan an observable against the Passive DNS Farsight Database (support both v1 and v2 versions)
                          • DNS0_EU: Retrieve current domain resolution with DNS0.eu DoH (DNS over HTTPS)
                          • DNS0_EU_Malicious_Detector: Check if a domain or an url is marked as malicious in DNS0.eu database (Zero service)
                          • DocGuard_Get: check if an hash was analyzed on DocGuard. DocGuard
                          • Feodo_Tracker: Feodo Tracker offers various blocklists, helping network owners to protect their users from Dridex and Emotet/Heodo.
                          • FileScan_Search: Finds reports and uploaded files by various tokens, like hash, filename, verdict, IOCs etc via FileScan.io API.
                          • FireHol_IPList: check if an IP is in FireHol's IPList
                          • GoogleSafebrowsing: Scan an observable against GoogleSafeBrowsing DB
                          • GoogleWebRisk: Scan an observable against WebRisk API (Commercial version of Google Safe Browsing). Check the docs to enable this properly
                          • Google_DNS: Retrieve current domain resolution with Google DoH (DNS over HTTPS)
                          • GreedyBear: scan an IP or a domain against the GreedyBear API (requires API key)
                          • GreyNoise: scan an IP against the Greynoise API (requires API key)
                          • GreyNoiseCommunity: scan an IP against the Community Greynoise API (requires API key))
                          • Greynoise_Labs: scan an IP against the Greynoise API (requires authentication token which can be obtained from cookies on Greynoise website after launching the playground from here)
                          • HashLookupServer_Get_Observable: check if a md5 or sha1 is available in the database of known file hosted by CIRCL
                          • HoneyDB_Get: HoneyDB IP lookup service
                          • HoneyDB_Scan_Twitter: scan an IP against HoneyDB.io's Twitter Threat Feed
                          • Hunter_How: Scans IP and domain against Hunter_How API.
                          • Hunter_Io: Scans a domain name and returns set of data about the organisation, the email address found and additional information about the people owning those email addresses.
                          • HybridAnalysis_Get_Observable: search an observable in the HybridAnalysis sandbox reports
                          • IP2WHOIS: API Docs IP2Location.io IP2WHOIS Domain WHOIS API helps users to obtain domain information and WHOIS record by using a domain name.
                          • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
                          • InQuest_DFI: Deep File Inspection by InQuest Labs
                          • InQuest_IOCdb: Indicators of Compromise Database by InQuest Labs
                          • InQuest_REPdb: Search in InQuest Lab's Reputation Database
                          • IPApi: Get information about IPs using batch-endpoint and DNS using DNS-endpoint.
                          • IPInfo: Location Information about an IP
                          • Ip2location: API Docs IP2Location.io allows users to check IP address location in real time. (Supports both with or without key)
                          • Intezer_Get: check if an analysis related to a hash is available in Intezer. Register for a free community account here.
                          • Koodous: koodous API get information about android malware.
                          • MalwareBazaar_Get_Observable: Check if a particular malware hash is known to MalwareBazaar
                          • MalwareBazaar_Google_Observable: Check if a particular IP, domain or url is known to MalwareBazaar using google search
                          • MaxMindGeoIP: extract GeoIP info for an observable
                          • MISP: scan an observable on a MISP instance
                          • MISPFIRST: scan an observable on the FIRST MISP instance
                          • Mmdb_server: Mmdb_server mmdb-server is an open source fast API server to lookup IP addresses for their geographic location, AS number.
                          • Mnemonic_PassiveDNS : Look up a domain or IP using the Mnemonic PassiveDNS public API.
                          • MWDB_Get: mwdblib Retrieve malware file analysis by hash from repository maintained by CERT Polska MWDB.
                          • Netlas: search an IP against Netlas
                          • ONYPHE: search an observable in ONYPHE
                          • OpenCTI: scan an observable on an OpenCTI instance
                          • OTXQuery: scan an observable on Alienvault OTX
                          • Phishstats: Search PhishStats API to determine if an IP/URL/domain is malicious.
                          • Phishtank: Search an url against Phishtank API
                          • PhishingArmy: Search an observable in the PhishingArmy blocklist
                          • Pulsedive: Scan indicators and retrieve results from Pulsedive's API.
                          • Quad9_DNS: Retrieve current domain resolution with Quad9 DoH (DNS over HTTPS)
                          • Quad9_Malicious_Detector: Leverages Quad9 DoH to check if a domain is related to malware
                          • Robtex: scan a domain/IP against the Robtex Passive DNS DB
                          • Securitytrails: scan an IP/Domain against Securitytrails API
                          • Shodan_Honeyscore: scan an IP against Shodan Honeyscore API
                          • Shodan_Search: scan an IP against Shodan Search API
                          • Spyse: Scan domains, IPs, emails and CVEs using Spyse's API. Register here.
                          • SSAPINet: get a screenshot of a web page using screenshotapi.net (external source); additional config options can be added to extra_api_params in the config.
                          • Stalkphish: Search Stalkphish API to retrieve information about a potential phishing site (IP/URL/domain/Generic).
                          • Stratosphere_Blacklist: Cross-reference an IP from blacklists maintained by Stratosphere Labs
                          • TalosReputation: check an IP reputation from Talos
                          • ThreatFox: search for an IOC in ThreatFox's database
                          • Threatminer: retrieve data from Threatminer API
                          • TorNodesDanMeUk: check if an IP is a Tor Node using a list of all Tor nodes provided by dan.me.uk
                          • TorProject: check if an IP is a Tor Exit Node
                          • Triage_Search: Search for reports of observables or upload from URL on triage cloud
                          • Tranco: Check if a domain is in the latest Tranco ranking top sites list
                          • URLhaus: Query a domain or URL against URLhaus API.
                          • UrlScan_Search: Search an IP/domain/url/hash against URLScan API
                          • UrlScan_Submit_Result: Submit & retrieve result of an URL against URLScan API
                          • Virushee_CheckHash: Search for a previous analysis of a file by its hash (SHA256/SHA1/MD5) on Virushee API.
                          • VirusTotal_v3_Get_Observable: search an observable in the VirusTotal DB
                          • Whoisxmlapi: Fetch WHOIS record data, of a domain name, an IP address, or an email address.
                          • WhoIs_RipeDB_Search : Fetch whois record data of an IP address from Ripe DB using their search API (no API key required)
                          • XForceExchange: scan an observable on IBM X-Force Exchange
                          • YARAify_Search: lookup a file hash in Abuse.ch YARAify
                          • YETI (Your Everyday Threat Intelligence): scan an observable on a YETI instance.
                          • Zoomeye: Zoomeye Cyberspace Search Engine recording information of devices, websites, services and components etc..
                          • Validin:Validin investigates historic and current data describing the structure and composition of the internet.
                          • TweetFeed: TweetFeed collects Indicators of Compromise (IOCs) shared by the infosec community at Twitter.\\r\\nHere you will find malicious URLs, domains, IPs, and SHA256/MD5 hashes.
                          • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
                          • CyCat: CyCat or the CYbersecurity Resource CATalogue aims at mapping and documenting, in a single formalism and catalogue available cybersecurity tools, rules, playbooks, processes and controls.
                          • Vulners: Vulners is the most complete and the only fully correlated security intelligence database, which goes through constant updates and links 200+ data sources in a unified machine-readable format. It contains 8 mln+ entries, including CVEs, advisories, exploits, and IoCs \u2014 everything you need to stay abreast on the latest security threats.
                          • AILTypoSquatting:AILTypoSquatting is a Python library to generate list of potential typo squatting domains with domain name permutation engine to feed AIL and other systems.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#generic-analyzers-email-phone-number-etc-anything-really","title":"Generic analyzers (email, phone number, etc.; anything really)","text":"

                          Some analyzers require details other than just IP, URL, Domain, etc. We classified them as generic Analyzers. Since the type of field is not known, there is a format for strings to be followed.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#internal-tools_2","title":"Internal tools","text":"
                          • CyberChef: Run a query on a CyberChef server using pre-defined or custom recipes.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#external-services_2","title":"External services","text":"
                          • Anomali_Threatstream_Confidence: Give max, average and minimum confidence of maliciousness for an observable. On Anomali Threatstream Confidence API.
                          • Anomali_Threatstream_Intelligence: Search for threat intelligence information about an observable. On Anomali Threatstream Intelligence API.
                          • CRXcavator: scans a chrome extension against crxcavator.io
                          • Dehashed_Search: Query any observable/keyword against https://dehashed.com's search API.
                          • EmailRep: search an email address on emailrep.io
                          • HaveIBeenPwned: HaveIBeenPwned checks if an email address has been involved in a data breach
                          • IntelX_Intelligent_Search: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
                          • IntelX_Phonebook: IntelligenceX is a search engine and data archive. Fetches emails, urls, domains associated with an observable or a generic string.
                          • IPQS_Fraud_And_Risk_Scoring: Scan an Observable against IPQualityscore
                          • MISP: scan an observable on a MISP instance
                          • VirusTotal_v3_Intelligence_Search: Perform advanced queries with VirusTotal Intelligence (requires paid plan)
                          • WiGLE: Maps and database of 802.11 wireless networks, with statistics, submitted by wardrivers, netstumblers, and net huggers.
                          • YARAify_Generics: lookup a YARA rule (default), ClamAV rule, imphash, TLSH, telfhash or icon_dash in YARAify
                          • PhoneInfoga : PhoneInfoga is one of the most advanced tools to scan international phone numbers.
                          • HudsonRock: Hudson Rock provides its clients the ability to query a database of over 27,541,128 computers which were compromised through global info-stealer campaigns performed by threat actors.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#optional-analyzers","title":"Optional analyzers","text":"

                          Some analyzers are optional and need to be enabled explicitly.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#connectors","title":"Connectors","text":"

                          Connectors are designed to run after every successful analysis which makes them suitable for automated threat-sharing. They support integration with other SIEM/SOAR projects, specifically aimed at Threat Sharing Platforms.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#connectors-list","title":"Connectors list","text":"

                          The following is the list of the available connectors. You can also navigate the same list via the

                          • Graphical Interface: once your application is up and running, go to the \"Plugins\" section
                          • pyintelowl: $ pyintelowl get-connector-config
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#list-of-pre-built-connectors","title":"List of pre-built Connectors","text":"
                          • MISP: automatically creates an event on your MISP instance, linking the successful analysis on IntelOwl.
                          • OpenCTI: automatically creates an observable and a linked report on your OpenCTI instance, linking the the successful analysis on IntelOwl.
                          • YETI: YETI = Your Everyday Threat Intelligence. find or create observable on YETI, linking the successful analysis on IntelOwl.
                          • Slack: Send the analysis link to a Slack channel (useful for external notifications)
                          • EmailSender: Send a generic email.
                          • AbuseSubmitter: Send an email to request to take down a malicious domain.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#pivots","title":"Pivots","text":"

                          With IntelOwl v5.2.0 we introduced the Pivot Plugin.

                          Pivots are designed to create a job from another job. This plugin allows the user to set certain conditions that trigger the execution of one or more subsequent jobs, strictly connected to the first one.

                          This is a \"SOAR\" feature that allows the users to connect multiple analysis together.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#list-of-pre-built-pivots","title":"List of pre-built Pivots","text":"
                          • TakedownRequestToAbuseIp: This Plugin leverages results from DNS resolver analyzers to extract a valid IP address to pivot to the Abusix analyzer.
                          • AbuseIpToSubmission: This Plugin leverages results from the Abusix analyzer to extract the abuse contacts of an IP address to pivot to the AbuseSubmitter connector.

                          You can build your own custom Pivot with your custom logic with just few lines of code. See the Contribute section for more info.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#creating-pivots-from-the-gui","title":"Creating Pivots from the GUI","text":"

                          From the GUI, the users can pivot in two ways: - If a Job executed a Visualizer, it is possible to select a field extracted and analyze its value by clicking the \"Pivot\" button (see following image). In this way, the user is able to \"jump\" from one indicator to another.

                          • Starting from an already existing Investigation, it is possible to select a Job block and click the \"Pivot\" button to analyze the same observable again, usually choosing another Playbook (see following image)

                          In both cases, the user is redirected to the Scan Page that is precompiled with the observable selected. Then the user would be able to select the Playbook to execute in the new job.

                          After the new Job is started, a new Investigation will be created (if it does not already exist) and both the jobs will be added to the same Investigation.

                          In the following image you can find an example of an Investigation composed by 3 pivots generated manually: * leveraging the first way to create a Pivot, the 2 Jobs that analyzed IP addresses have been generated from the first test\\.com Job * leveraging the second way to create a Pivot, the second test\\.com analysis had been created with a different Playbook.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#visualizers","title":"Visualizers","text":"

                          With IntelOwl v5 we introduced a new plugin type called Visualizers. You can leverage it as a framework to create custom aggregated and simplified visualization of analyzer results.

                          Visualizers are designed to run after the analyzers and the connectors. The visualizer adds logic after the computations, allowing to show the final result in a different way than merely the list of reports.

                          Visualizers can be executed only during Scans through the playbook that has been configured on the visualizer itself.

                          This framework is extremely powerful and allows every user to customize the GUI as they wish. But you know...with great power comes great responsability. To fully leverage this framework, you would need to put some effort in place. You would need to understand which data is useful for you and then write few code lines that would create your own GUI. To simplify the process, take example from the pre-built visualizers listed below and follow the dedicated documentation.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#list-of-pre-built-visualizers","title":"List of pre-built Visualizers","text":"
                          • DNS: displays the aggregation of every DNS analyzer report
                          • Yara: displays the aggregation of every matched rule by the Yara Analyzer
                          • Domain_Reputation: Visualizer for the Playbook \"Popular_URL_Reputation_Services\"
                          • IP_Reputation: Visualizer for the Playbook \"Popular_IP_Reputation_Services\"
                          • Pivot: Visualizer that can be used in a Playbook to show the Pivot execution result. See Pivots for more info.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#ingestors","title":"Ingestors","text":"

                          With IntelOwl v5.1.0 we introduced the Ingestor Plugin.

                          Ingestors allow to automatically insert IOC streams from outside sources to IntelOwl itself. Each Ingestor must have a Playbook attached: this will allow to create a Job from every IOC retrieved.

                          Ingestors are system-wide and disabled by default, meaning that only the administrator are able to configure them and enable them. Ingestors can be spammy so be careful about enabling them.

                          A very powerful use is case is to combine Ingestors with Connectors to automatically extract data from external sources, analyze them with IntelOwl and push them externally to another platform (like MISP or a SIEM)

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#list-of-pre-built-ingestors","title":"List of pre-built Ingestors","text":"
                          • ThreatFox: Retrieves daily ioc from https://threatfox.abuse.ch/ and analyze them.
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#playbooks","title":"Playbooks","text":"

                          Playbooks are designed to be easy to share sequence of running Plugins (Analyzers, Connectors, ...) on a particular kind of observable.

                          If you want to avoid to re-select/re-configure a particular combination of analyzers and connectors together every time, you should create a playbook out of it and use it instead. This is time saver.

                          This is a feature introduced since IntelOwl v4.1.0! Please provide feedback about it!

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#playbooks-list","title":"Playbooks List","text":"

                          The following is the list of the available pre-built playbooks. You can also navigate the same list via the

                          • Graphical Interface: once your application is up and running, go to the \"Plugins\" section
                          • pyintelowl: $ pyintelowl get-playbook-config
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#list-of-pre-built-playbooks","title":"List of pre-built playbooks","text":"
                          • FREE_TO_USE_ANALYZERS: A playbook containing all free to use analyzers.
                          • Sample_Static_Analysis: A playbook containing all analyzers that perform static analysis on files.
                          • Popular_URL_Reputation_Services: Collection of the most popular and free reputation analyzers for URLs and Domains
                          • Popular_IP_Reputation_Services: Collection of the most popular and free reputation analyzers for IP addresses
                          • Dns: A playbook containing all dns providers
                          • Takedown_Request: Start investigation to request to take down a malicious domain. A mail will be sent to the domain's abuse contacts found
                          • Abuse_IP: Playbook containing the Abusix analyzer. It is executed after the Takedown_Request playbook
                          • Send_Abuse_Email: Playbook containing the AbuseSubmitter connector to send an email to request to take down a malicious domain. It is executed after the Abuse_IP playbook
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#playbooks-creation-and-customization","title":"Playbooks creation and customization","text":"

                          You can create new playbooks in different ways, based on the users you want to share them with:

                          If you want to share them to every user in IntelOwl, create them via the Django Admin interface at /admin/playbooks_manager/playbookconfig/.

                          If you want share them to yourself or your organization only, you need to leverage the \"Save as Playbook\" button that you can find on the top right of the Job Result Page. In this way, after you have done an analysis, you can save the configuration of the Plugins you executed for re-use with a single click.

                          The created Playbook would be available to yourself only. If you want either to share it with your organization or to delete it, you need to go to the \"Plugins\" section and enable it manually by clicking the dedicated button.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#generic-plugin-creation-configuration-and-customization","title":"Generic Plugin Creation, Configuration and Customization","text":"

                          If you want to create completely new Plugins (not based on already existing python modules), please refer to the Contribute section. This is usually the case when you want to integrate IntelOwl with either a new tool or a new service.

                          On the contrary, if you would like to just customize the already existing plugins, this is the place.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#superuser-customization","title":"SuperUser customization","text":"

                          If you are an IntelOwl superuser, you can create, modify, delete analyzers based on already existing modules by changing the configuration values inside the Django Admin interface at: - for analyzers: /admin/analyzers_manager/analyzerconfig/. - for connectors: /admin/connectors_manager/connectorconfig/. - ...and so on for all the Plugin types.

                          The following are the most important fields that you can change without touching the source code: - Name: Name of the analyzer - Description: Description of the analyzer - Disabled: you can choose to disable certain analyzers, then they won't appear in the dropdown list and won't run if requested. - Python Module: Python path of the class that will be executed. This should not be changed most of the times. - Maximum TLP: see TLP Support - Soft Time Limit: this is the maximum time (in seconds) of execution for an analyzer. Once reached, the task will be killed (or managed in the code by a custom Exception). Default 300. - Routing Key: this takes effects only when multi-queue is enabled. Choose which celery worker would execute the task: local (ideal for tasks that leverage local applications like Yara), long (ideal for long tasks) or default (ideal for simple webAPI-based analyzers).

                          For analyzers only: - Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a different mimetype from the ones you specified, it won't be executed - Not Supported Filetypes: can be populated as a list. If set, if you ask to analyze a file with a mimetype from the ones you specified, it won't be executed - Observable Supported: can be populated as a list. If set, if you ask to analyze an observable that is not in this list, it won't be executed. Valid values are: ip, domain, url, hash, generic.

                          For connectors only: - Run on Failure (default: true): if they can be run even if the job has status reported_with_fails

                          For visualizers only: - Playbooks: list of playbooks that trigger the specified visualizer execution.

                          Sometimes, it may happen that you would like to create a new analyzer very similar to an already existing one. Maybe you would like to just change the description and the default parameters. A helpful way to do that without having to copy/pasting the entire configuration, is to click on the analyzer that you want to copy, make the desired changes, and click the save as new button.

                          Warning

                          Changing other keys can break a plugin. In that case, you should think about duplicating the configuration entry or python module with your changes.

                          Other options can be added at the \"Python module\" level and not at the Plugin level. To do that, go to: admin/api_app/pythonmodule/ and select the Python module used by the Plugin that you want to change. For example, the analyzer AbuseIPDB uses the Python module abuseipdb.AbuseIPDB.

                          Once there, you'll get this screen:

                          There you can change the following values: - Update Schedule: if the analyzer require some sort of update (local database, local rules, ...), you can specify the crontab schedule to update them. - Health Check Schedule: if the analyzer has implemented a Health Check, you can specify the crontab schedule to check whether the service works or not.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#parameters","title":"Parameters","text":"

                          Each Plugin could have one or more parameters available to be configured. These parameters allow the users to customize the Plugin behavior.

                          There are 2 types of Parameters: * classic Parameters * Secrets: these parameters usually manage sensitive data, like API keys.

                          To see the list of these parameters:

                          • You can view the \"Plugin\" Section in IntelOwl to have a complete and updated view of all the options available
                          • You can view the parameters by exploring the Django Admin Interface:
                          • admin/api_app/parameter/
                          • or at the very end of each Plugin configuration like /admin/analyzers_manager/analyzerconfig/

                          You can change the Plugin Parameters at 5 different levels: * if you are an IntelOwl superuser, you can go in the Django Admin Interface and change the default values of the parameters for every plugin you like. This option would change the default behavior for every user in the platform. * if you are either Owner or Admin of an org, you can customize the default values of the parameters for every member of the organization by leveraging the GUI in the \"Organization Config\" section. This overrides the previous option. * if you are a normal user, you can customize the default values of the parameters for your analysis only by leveraging the GUI in the \"Plugin config\" section. This overrides the previous option. * You can choose to provide runtime configuration when requesting an analysis that will override the previous options. This override is done only for the specific analysis. See Customize analyzer execution at time of request

                          Playbook Exception

                          Please remember that, if you are executing a Playbook, the \"Runtime configuration\" of the Playbook take precedence over the Plugin Configuration.

                          Plugin Configuration Order

                          Due to the multiple chances that are given to customize the parameters of the Plugins that are executed, it may be easy to confuse the order and launch Plugins without the awereness of what you are doing. This is the order to define which values are used for the parameters, starting by the most important element: * Runtime Configuration at Time of Request. * Runtime Configuration of the Playbook (if a Playbook is used and the Runtime Configuration at Time of Request is empty) * Plugin Configuration of the User * Plugin Configuration of the Organization * Default Plugin Configuration of the Parameter If you are using the GUI, please remember that you can always check the Parameters before starting a \"Scan\" by clicking at the \"Runtime configuration\" ![img.png](../static/runtime_config.png) button. Example: ![img.png](../static/runtime_config_2.png)"},{"location":"Submodules/IntelOwl/docs/source/Usage/#enabling-or-disabling-plugins","title":"Enabling or Disabling Plugins","text":"

                          By default, each available plugin is configured as either disabled or not. The majority of them are enabled by default, while others may be disabled to avoid potential problems in the application usability for first time users.

                          Considering the impact that this change could have in the application, the GUI does not allow a normal user to enable/disable any plugin. On the contrary, users with specific privileges may change this configuration: * Org Administrators may leverage the feature documented here to enable/disable plugins for their org. This can be helpful to control users' behavior. * IntelOwl Superusers (full admin) can go to the Django Admin Interface and enable/disable them from there. This operation does overwrite the Org administrators configuration. To find the plugin to change, they'll need to first choose the section of its type (\"ANALYZERS_MANAGER\", \"CONNECTORS_MANAGER\", etc), then select the chosen plugin, change the flag on that option and save the plugin by pressing the right button.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#special-plugins-operations","title":"Special Plugins operations","text":"

                          All plugins, i.e. analyzers and connectors, have kill and retry actions. In addition to that, all docker-based analyzers and connectors have a healthcheck action to check if their associated instances are up or not.

                          • kill:

                          Stop a plugin whose status is running/pending:

                          • GUI: Buttons on reports table on job result page.
                          • PyIntelOwl: IntelOwl.kill_analyzer and IntelOwl.kill_connector function.
                          • CLI: $ pyintelowl jobs kill-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs kill-connector <job_id> <connector_name>
                          • API: PATCH /api/job/{job_id}/{plugin_type/{plugin_name}/kill and PATCH /api/job/{job_id}/connector/{connector_name}/kill

                          • retry:

                          Retry a plugin whose status is failed/killed:

                          • GUI: Buttons on reports table on job result page.
                          • PyIntelOwl: IntelOwl.retry_analyzer and IntelOwl.retry_connector function,
                          • CLI: $ pyintelowl jobs retry-analyzer <job_id> <analyzer_name> and $ pyintelowl jobs retry-connector <job_id> <connector_name>
                          • API: PATCH /api/job/{job_id}/{plugin_type}/{plugin_name}/retry

                          • healthcheck:

                          Check if a plugin is able to connect to its provider:

                          • GUI: Buttons on every plugin table.
                          • PyIntelOwl: IntelOwl.analyzer_healthcheck and IntelOwl.connector_healthcheck methods.
                          • CLI: $ pyintelowl analyzer-healthcheck <analyzer_name> and $ pyintelowl connector-healthcheck <connector_name>
                          • API: GET /api/{plugin_type}/{plugin_name}/healthcheck

                          • pull:

                          Update a plugin with the newest rules/database:

                          • GUI: Buttons on every plugin table.
                          • API: POST /api/{plugin_type}/{plugin_name}/pull
                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#tlp-support","title":"TLP Support","text":"

                          The Traffic Light Protocol (TLP) is a standard that was created to facilitate greater sharing of potentially sensitive information and more effective collaboration.

                          IntelOwl is not a threat intel sharing platform, like the MISP platform. However, IntelOwl is able to share analysis results to external platforms (via Connectors) and to send possible privacy related information to external services (via Analyzers).

                          This is why IntelOwl does support a customized version of the Traffic Light Protocol (TLP): to allow the user to have a better knowledge of how their data are being shared.

                          Every Analyzer and Connector can be configured with a maximum_tlp value. Based on that value, IntelOwl understands if the specific plugin is allowed or not to run (e.g. if maximum_tlp is GREEN, it would run for analysis with TLPs WHITE and GREEN only)

                          These is how every available TLP value behaves once selected for an analysis execution: 1. CLEAR: no restriction (WHITE was replaced by CLEAR in TLP v2.0, but WHITE is supported for retrocompatibility) 2. GREEN: disable analyzers that could impact privacy 3. AMBER (default): disable analyzers that could impact privacy and limit view permissions to my group 4. RED: disable analyzers that could impact privacy, limit view permissions to my group and do not use any external service

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#running-a-plugin","title":"Running a plugin","text":"

                          A plugin can be run when all of the following requirements have been satisfied: 1. All the required parameters of the plugin have been configured 2. The plugin is not disabled 3. The plugin is not disabled for the user's organization 4. If the plugin has a health check schedule, the last check has to be successful 5. The TLP selected to run the plugin cannot be higher than the maximum TLP configured for that plugin 6. The observable classification or the file mimetype has to be supported by the plugin

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#investigations-framework","title":"Investigations Framework","text":"

                          Investigations are a new framework introduced in IntelOwl v6 with the goal to allow the users to connect the analysis they do with each other.

                          In this way the analysts can use IntelOwl as the starting point of their \"Investigations\", register their findings, correlate the information found, and collaborate...all in a single place.

                          Things to know about the framework: * an Investigation is a superset of IntelOwl Jobs. It can have attached one or more existing IntelOwl Jobs * an Investigation contains a \"description\" section that can be changed and updated at anytime with new information from the analysts. * modification to the Investigation (description, jobs, etc) can be done by every member of the Organization where the creator of the Investigation belongs. However only they creator can delete an Investigation.

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#create-and-populate-an-investigation","title":"Create and populate an investigation","text":"

                          Investigations are created in 2 ways: * automatically: * if you scan multiple observables at the same time, a new investigation will be created by default and all the observables they will be automatically connected to the same investigation. * if you run a Job with a Playbook which contains a Pivot that triggers another Job, a new investigation will be created and both the Jobs will be added to the same investigation. See how you can create a new Pivot manually from the GUI. * manually: by clicking on the button in the \"History\" section you can create an Investigation from scratch without any job attached (see following image)

                          If you want to add a job to an Investigation, you should click to the root block of the Investigation (see following image):

                          Once a job has been added, you'll have something like this:

                          If you want to remove a Job, you can click on the Job block and click \"Remove branch\". On the contrary, if you just want to see Job Results, you can click in the \"Link\" button. (check next image)

                          "},{"location":"Submodules/IntelOwl/docs/source/Usage/#example-output-of-a-complex-investigation","title":"Example output of a complex investigation","text":""},{"location":"Submodules/IntelOwl/frontend/","title":"IntelOwl - frontend","text":"

                          Built with @certego/certego-ui.

                          "},{"location":"Submodules/IntelOwl/frontend/#design-thesis","title":"Design thesis","text":"
                          • Re-usable components/hooks/stores that other projects can also benefit from should be added to certego-ui package.
                          • IntelOwl specific:
                          • components should be added to src/components/common.
                          • general hooks should be added to src/hooks.
                          • zustand stores hooks should be added to src/stores.
                          "},{"location":"Submodules/IntelOwl/frontend/#directory-structure","title":"Directory Structure","text":"
                          public/                                   public static assets\n|- icons/                                 icons/favicon\n|- index.html/                            root HTML file\nsrc/                                      source code\n|- components/                            pages and components\n|  |- auth/                               `authentication` (login, logout, OAuth pages)\n|  |- common/                             small re-usable components\n|  |- dashboard/                          dashboard page and charts\n|  |- home/                               landing/home page\n|  |- jobs/                               `api_app`\n|  |  |- result/                          JobResult.jsx\n|  |  |- table/                           JobsTable.jsx\n|  |- me/\n|  |  |- organization/                    `certego_saas.apps.organization`\n|  |  |- sessions/                        durin (sessions management)\n|  |- misc/\n|  |  |- notification/                    `certego_saas.apps.notifications`\n|  |- plugins/                            `api_app.analyzers_manager`, `api_app.connectors_manager`\n|  |- scan/                               new scan/job\n|  |- Routes.jsx                          lazy route-component mappings\n|- constants/                             constant values\n|  |- api.js                              API URLs\n|  |- environment.js                      environment variables\n|  |- index.js                            intelowl specific constants\n|- hooks/                                 react hooks\n|- layouts/                               header, main, footer containers\n|- stores/                                zustand stores hooks\n|- styles/                                scss files\n|- utils/                                 utility functions\n|- wrappers/                              Higher-Order components\n|- App.jsx                                App component\n|- index.jsx                              Root JS file (ReactDOM renderer)\n
                          "},{"location":"Submodules/IntelOwl/frontend/#local-development-environment","title":"Local Development Environment","text":"

                          The frontend inside the docker containers does not hot-reload, so you need to use CRA dev server on your host machine to serve pages when doing development on the frontend, using docker nginx only as API source.

                          • Start IntelOwl containers (see docs). Original dockerized app is accessible on http://localhost:80

                          • If you have not node-js installed, you have to do that. Follow the guide here. We tested this with NodeJS >=16.6

                          • Install npm packages locally

                          cd ./frontend && npm install\n
                          • Start CRA dev server:
                          npm start\n
                          • Now you can access the auto-reloading frontend on http://localhost:3000. It acts as proxy for API requests to original app web server.

                          • JS app main configs are available in package.json.

                          • (optional) Use local build of certego-ui package so it can also hot-reload. This is useful when you want to make changes in certego-ui and rapidly test them with IntelOwl. Refer here for setup instructions.

                          "},{"location":"Submodules/IntelOwl/frontend/#miscellaneous","title":"Miscellaneous","text":""},{"location":"Submodules/IntelOwl/frontend/#dependabot","title":"Dependabot","text":"

                          We have dependabot enabled for the React.js frontend application. The updates are scheduled for once a week.

                          "},{"location":"Submodules/IntelOwl/frontend/#external-docs","title":"External Docs","text":"
                          • Create React App documentation.
                          • React documentation.
                          "},{"location":"Submodules/IntelOwl/integrations/malware_tools_analyzers/qiling/profiles/","title":"Please add your profile files here","text":""},{"location":"Submodules/IntelOwl/integrations/malware_tools_analyzers/qiling/rootfs/","title":"Please add your rootfs folder here","text":""},{"location":"Submodules/pyintelowl/","title":"PyIntelOwl","text":"

                          Robust Python SDK and Command Line Client for interacting with IntelOwl's API.

                          "},{"location":"Submodules/pyintelowl/#features","title":"Features","text":"
                          • Easy one-time configuration with self documented help and hints along the way.
                          • Request new analysis for observables and files.
                          • Select which analyzers you want to run for every analysis you perform.
                          • Choose whether you want to HTTP poll for the analysis to finish or not.
                          • List all jobs or view one job in a prettified tabular form.
                          • List all tags or view one tag in a prettified tabular form.
                          "},{"location":"Submodules/pyintelowl/#demo","title":"Demo","text":""},{"location":"Submodules/pyintelowl/#installation","title":"Installation","text":"
                          $ pip3 install pyintelowl\n

                          For development/testing, pip3 install pyintelowl[dev]

                          "},{"location":"Submodules/pyintelowl/#quickstart","title":"Quickstart","text":""},{"location":"Submodules/pyintelowl/#as-command-line-client","title":"As Command Line Client","text":"

                          On successful installation, The pyintelowl entryscript should be directly invokable. For example,

                          $ pyintelowl\nUsage: pyintelowl [OPTIONS] COMMAND [ARGS]...\n\nOptions:\n  -d, --debug  Set log level to DEBUG\n  --version    Show the version and exit.\n  -h, --help   Show this message and exit.\n\nCommands:\n  analyse                Send new analysis request\n  analyzer-healthcheck   Send healthcheck request for an analyzer...\n  config                 Set or view config variables\n  connector-healthcheck  Send healthcheck request for a connector\n  jobs                   Manage Jobs\n  tags                   Manage tags\n
                          "},{"location":"Submodules/pyintelowl/#as-a-library-sdk","title":"As a library / SDK","text":"
                          from pyintelowl import IntelOwl\nobj = IntelOwl(\"<your_api_key>\", \"<your_intelowl_instance_url>\", \"optional<path_to_pem_file>\", \"optional<proxies>\")\n

                          For more comprehensive documentation, please see https://intelowlproject.github.io/docs/pyintelowl/.

                          "},{"location":"Submodules/pyintelowl/#changelog","title":"Changelog","text":"

                          View CHANGELOG.md.

                          "},{"location":"Submodules/pyintelowl/#how-to-generate-an-api-key","title":"How to generate an API key","text":"

                          You need a valid API key to interact with the IntelOwl server. Keys can be created from the \"API access\" section of the user's menu in the IntelOwl's GUI.

                          Otherwise, you can create them from the Django Admin Interface of the IntelOwl application with an administration account. Section \"Durin\" -> \"Auth tokens\"

                          "},{"location":"pyintelowl/","title":"Quickstart","text":"

                          PyIntelOwl Repository

                          "},{"location":"pyintelowl/#welcome-to-pyintelowls-documentation","title":"Welcome to PyIntelOwl's documentation!","text":""},{"location":"pyintelowl/#robust-python-sdk-and-command-line-client-for-interacting-with-intelowl-api","title":"Robust Python SDK and Command Line Client for interacting with IntelOwl API.","text":""},{"location":"pyintelowl/#installation","title":"Installation","text":"
                          pip install pyintelowl\n
                          "},{"location":"pyintelowl/#usage-as-cli","title":"Usage as CLI","text":"
                           pyintelowl\n Usage: pyintelowl [OPTIONS] COMMAND [ARGS]...\n\n Options:\n -d, --debug  Set log level to DEBUG\n --version    Show the version and exit.\n -h, --help   Show this message and exit.\n\n Commands:\n analyse                Send new analysis request\n analyzer-healthcheck   Send healthcheck request for an analyzer...\n config                 Set or view config variables\n connector-healthcheck  Send healthcheck request for a connector\n get-analyzer-config    Get current state of `analyzer_config.json` from...\n get-connector-config   Get current state of `connector_config.json` from...\n get-playbook-config    Get current state of `playbook_config.json` from...\n jobs                   Manage Jobs\n tags                   Manage tags\n
                          "},{"location":"pyintelowl/#configuration","title":"Configuration:","text":"

                          You can use set to set the config variables and get to view them.

                          pyintelowl config set -k 4bf03f20add626e7138f4023e4cf52b8 -u \"http://localhost:80\"\npyintelowl config get\n
                          "},{"location":"pyintelowl/#hint","title":"Hint","text":"

                          The CLI would is well-documented which will help you navigate various commands easily. Invoke pyintelowl -h or pyintelowl <command> -h to get help.

                          "},{"location":"pyintelowl/#usage-as-sdklibrary","title":"Usage as SDK/library","text":"
                           from pyintelowl import IntelOwl, IntelOwlClientException\n obj = IntelOwl(\n    \"4bf03f20add626e7138f4023e4cf52b8\",\n    \"http://localhost:80\",\n    None,\n )\n \"\"\"\n obj = IntelOwl(\n    \"<your_api_key>\",\n    \"<your_intelowl_instance_url>\",\n    \"optional<path_to_pem_file>\"\n    \"optional<proxies>\"\n )\n \"\"\"\n\n try:\n    ans = obj.get_analyzer_configs()\n    print(ans)\n except IntelOwlClientException as e:\n    print(\"Oh no! Error: \", e)\n
                          "},{"location":"pyintelowl/#tip","title":"Tip","text":"

                          We very much recommend going through the :class:pyintelowl.pyintelowl.IntelOwl docs.

                          "},{"location":"pyintelowl/#index","title":"Index","text":"
                          .. toctree::\n   :maxdepth: 2\n   :caption: Usage\n\n   pyintelowl\n
                            .. toctree::\n   :maxdepth: 2\n   :caption: Development\n\n   tests\n
                          "},{"location":"pyintelowl/IntelOwlClass/","title":"IntelOwlClass","text":""},{"location":"pyintelowl/IntelOwlClass/#intelowl-class","title":"IntelOwl Class","text":"Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          class IntelOwl:\n    logger: logging.Logger\n\n    def __init__(\n        self,\n        token: str,\n        instance_url: str,\n        certificate: str = None,\n        proxies: dict = None,\n        logger: logging.Logger = None,\n        cli: bool = False,\n    ):\n        self.token = token\n        self.instance = instance_url\n        self.certificate = certificate\n        if logger:\n            self.logger = logger\n        else:\n            self.logger = logging.getLogger(__name__)\n        if proxies and not isinstance(proxies, dict):\n            raise TypeError(\"proxies param must be a dictionary\")\n        self.proxies = proxies\n        self.cli = cli\n\n    @property\n    def session(self) -> requests.Session:\n        \"\"\"\n        Internal use only.\n        \"\"\"\n        if not hasattr(self, \"_session\"):\n            session = requests.Session()\n            if self.certificate is not True:\n                session.verify = self.certificate\n            if self.proxies:\n                session.proxies = self.proxies\n            session.headers.update(\n                {\n                    \"Authorization\": f\"Token {self.token}\",\n                    \"User-Agent\": f\"PyIntelOwl/{__version__}\",\n                }\n            )\n            self._session = session\n\n        return self._session\n\n    def __make_request(\n        self,\n        method: Literal[\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"] = \"GET\",\n        *args,\n        **kwargs,\n    ) -> requests.Response:\n        \"\"\"\n        For internal use only.\n        \"\"\"\n        response: requests.Response = None\n        requests_function_map: Dict[str, Callable] = {\n            \"GET\": self.session.get,\n            \"POST\": self.session.post,\n            \"PUT\": self.session.put,\n            \"PATCH\": self.session.patch,\n            \"DELETE\": self.session.delete,\n        }\n        func = requests_function_map.get(method, None)\n        if not func:\n            raise RuntimeError(f\"Unsupported method name: {method}\")\n\n        try:\n            response = func(*args, **kwargs)\n            self.logger.debug(\n                msg=(response.url, response.status_code, response.content)\n            )\n            response.raise_for_status()\n        except Exception as e:\n            raise IntelOwlClientException(e, response=response)\n\n        return response\n\n    def ask_analysis_availability(\n        self,\n        md5: str,\n        analyzers: List[str] = None,\n        check_reported_analysis_too: bool = False,\n        minutes_ago: int = None,\n    ) -> Dict:\n        \"\"\"Search for already available analysis.\\n\n        Endpoint: ``/api/ask_analysis_availability``\n\n        Args:\n            md5 (str): md5sum of the observable or file\n            analyzers (List[str], optional):\n            list of analyzers to trigger.\n            Defaults to `None` meaning automatically select all configured analyzers.\n            check_reported_analysis_too (bool, optional):\n            Check against all existing jobs. Defaults to ``False``.\n            minutes_ago (int, optional):\n            number of minutes to check back for analysis.\n            Default is None so the check does not have any time limits.\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Dict: JSON body\n        \"\"\"\n        if not analyzers:\n            analyzers = []\n        data = {\"md5\": md5, \"analyzers\": analyzers}\n        if not check_reported_analysis_too:\n            data[\"running_only\"] = True\n        if minutes_ago:\n            data[\"minutes_ago\"] = int(minutes_ago)\n        url = self.instance + \"/api/ask_analysis_availability\"\n        response = self.__make_request(\"POST\", url=url, data=data)\n        answer = response.json()\n        status, job_id = answer.get(\"status\", None), answer.get(\"job_id\", None)\n        # check sanity cases\n        if not status:\n            raise IntelOwlClientException(\n                \"API ask_analysis_availability gave result without status ?\"\n                f\" Response: {answer}\"\n            )\n        if status != \"not_available\" and not job_id:\n            raise IntelOwlClientException(\n                \"API ask_analysis_availability gave result without job_id ?\"\n                f\" Response: {answer}\"\n            )\n        return answer\n\n    def send_file_analysis_request(\n        self,\n        filename: str,\n        binary: bytes,\n        tlp: TLPType = \"CLEAR\",\n        analyzers_requested: List[str] = None,\n        connectors_requested: List[str] = None,\n        runtime_configuration: Dict = None,\n        tags_labels: List[str] = None,\n    ) -> Dict:\n        \"\"\"Send analysis request for a file.\\n\n        Endpoint: ``/api/analyze_file``\n\n        Args:\n\n            filename (str):\n                Filename\n            binary (bytes):\n                File contents as bytes\n            analyzers_requested (List[str], optional):\n                List of analyzers to invoke\n                Defaults to ``[]`` i.e. all analyzers.\n            connectors_requested (List[str], optional):\n                List of specific connectors to invoke.\n                Defaults to ``[]`` i.e. all connectors.\n            tlp (str, optional):\n                TLP for the analysis.\n                (options: ``CLEAR, GREEN, AMBER, RED``).\n            runtime_configuration (Dict, optional):\n                Overwrite configuration for analyzers. Defaults to ``{}``.\n            tags_labels (List[str], optional):\n                List of tag labels to assign (creates non-existing tags)\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Dict: JSON body\n        \"\"\"\n        try:\n            if not tlp:\n                tlp = \"CLEAR\"\n            if not analyzers_requested:\n                analyzers_requested = []\n            if not connectors_requested:\n                connectors_requested = []\n            if not tags_labels:\n                tags_labels = []\n            if not runtime_configuration:\n                runtime_configuration = {}\n            data = {\n                \"file_name\": filename,\n                \"analyzers_requested\": analyzers_requested,\n                \"connectors_requested\": connectors_requested,\n                \"tlp\": tlp,\n                \"tags_labels\": tags_labels,\n            }\n            if runtime_configuration:\n                data[\"runtime_configuration\"] = json.dumps(runtime_configuration)\n            files = {\"file\": (filename, binary)}\n            answer = self.__send_analysis_request(data=data, files=files)\n        except Exception as e:\n            raise IntelOwlClientException(e)\n        return answer\n\n    def send_file_analysis_playbook_request(\n        self,\n        filename: str,\n        binary: bytes,\n        playbook_requested: str,\n        tlp: TLPType = \"CLEAR\",\n        runtime_configuration: Dict = None,\n        tags_labels: List[str] = None,\n    ) -> Dict:\n        \"\"\"Send playbook analysis request for a file.\\n\n        Endpoint: ``/api/playbook/analyze_multiple_files``\n\n        Args:\n\n            filename (str):\n                Filename\n            binary (bytes):\n                File contents as bytes\n            playbook_requested (str, optional):\n            tlp (str, optional):\n                TLP for the analysis.\n                (options: ``WHITE, GREEN, AMBER, RED``).\n            runtime_configuration (Dict, optional):\n                Overwrite configuration for analyzers. Defaults to ``{}``.\n            tags_labels (List[str], optional):\n                List of tag labels to assign (creates non-existing tags)\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Dict: JSON body\n        \"\"\"\n        try:\n            if not tags_labels:\n                tags_labels = []\n            if not runtime_configuration:\n                runtime_configuration = {}\n            data = {\n                \"playbook_requested\": playbook_requested,\n                \"tags_labels\": tags_labels,\n            }\n            # send this value only if populated,\n            # otherwise the backend would give you 400\n            if tlp:\n                data[\"tlp\"] = tlp\n\n            if runtime_configuration:\n                data[\"runtime_configuration\"] = json.dumps(runtime_configuration)\n            # `files` is wanted to be different from the other\n            # /api/analyze_file endpoint\n            # because the server is using different serializers\n            files = {\"files\": (filename, binary)}\n            answer = self.__send_analysis_request(\n                data=data, files=files, playbook_mode=True\n            )\n        except Exception as e:\n            raise IntelOwlClientException(e)\n        return answer\n\n    def send_observable_analysis_request(\n        self,\n        observable_name: str,\n        tlp: TLPType = \"CLEAR\",\n        analyzers_requested: List[str] = None,\n        connectors_requested: List[str] = None,\n        runtime_configuration: Dict = None,\n        tags_labels: List[str] = None,\n        observable_classification: str = None,\n    ) -> Dict:\n        \"\"\"Send analysis request for an observable.\\n\n        Endpoint: ``/api/analyze_observable``\n\n        Args:\n            observable_name (str):\n                Observable value\n            analyzers_requested (List[str], optional):\n                List of analyzers to invoke\n                Defaults to ``[]`` i.e. all analyzers.\n            connectors_requested (List[str], optional):\n                List of specific connectors to invoke.\n                Defaults to ``[]`` i.e. all connectors.\n            tlp (str, optional):\n                TLP for the analysis.\n                (options: ``CLEAR, GREEN, AMBER, RED``).\n            runtime_configuration (Dict, optional):\n                Overwrite configuration for analyzers. Defaults to ``{}``.\n            tags_labels (List[str], optional):\n                List of tag labels to assign (creates non-existing tags)\n            observable_classification (str):\n                Observable classification, Default to None.\n                By default launch analysis with an automatic classification.\n                (options: ``url, domain, hash, ip, generic``)\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n            IntelOwlClientException: on wrong observable_classification\n\n        Returns:\n            Dict: JSON body\n        \"\"\"\n        try:\n            if not tlp:\n                tlp = \"CLEAR\"\n            if not analyzers_requested:\n                analyzers_requested = []\n            if not connectors_requested:\n                connectors_requested = []\n            if not tags_labels:\n                tags_labels = []\n            if not runtime_configuration:\n                runtime_configuration = {}\n            if not observable_classification:\n                observable_classification = self._get_observable_classification(\n                    observable_name\n                )\n            elif observable_classification not in [\n                \"generic\",\n                \"hash\",\n                \"ip\",\n                \"domain\",\n                \"url\",\n            ]:\n                raise IntelOwlClientException(\n                    \"Observable classification only handle\"\n                    \" 'generic', 'hash', 'ip', 'domain' and 'url' \"\n                )\n            data = {\n                \"observable_name\": observable_name,\n                \"observable_classification\": observable_classification,\n                \"analyzers_requested\": analyzers_requested,\n                \"connectors_requested\": connectors_requested,\n                \"tlp\": tlp,\n                \"tags_labels\": tags_labels,\n                \"runtime_configuration\": runtime_configuration,\n            }\n            answer = self.__send_analysis_request(data=data, files=None)\n        except Exception as e:\n            raise IntelOwlClientException(e)\n        return answer\n\n    def send_observable_analysis_playbook_request(\n        self,\n        observable_name: str,\n        playbook_requested: str,\n        tlp: TLPType = \"CLEAR\",\n        runtime_configuration: Dict = None,\n        tags_labels: List[str] = None,\n        observable_classification: str = None,\n    ) -> Dict:\n        \"\"\"Send playbook analysis request for an observable.\\n\n        Endpoint: ``/api/playbook/analyze_multiple_observables``\n\n        Args:\n            observable_name (str):\n                Observable value\n            playbook_requested str:\n            tlp (str, optional):\n                TLP for the analysis.\n                (options: ``WHITE, GREEN, AMBER, RED``).\n            runtime_configuration (Dict, optional):\n                Overwrite configuration for analyzers. Defaults to ``{}``.\n            tags_labels (List[str], optional):\n                List of tag labels to assign (creates non-existing tags)\n            observable_classification (str):\n                Observable classification, Default to None.\n                By default launch analysis with an automatic classification.\n                (options: ``url, domain, hash, ip, generic``)\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n            IntelOwlClientException: on wrong observable_classification\n\n        Returns:\n            Dict: JSON body\n        \"\"\"\n        try:\n            if not tags_labels:\n                tags_labels = []\n            if not runtime_configuration:\n                runtime_configuration = {}\n            if not observable_classification:\n                observable_classification = self._get_observable_classification(\n                    observable_name\n                )\n            elif observable_classification not in [\n                \"generic\",\n                \"hash\",\n                \"ip\",\n                \"domain\",\n                \"url\",\n            ]:\n                raise IntelOwlClientException(\n                    \"Observable classification only handle\"\n                    \" 'generic', 'hash', 'ip', 'domain' and 'url' \"\n                )\n            data = {\n                \"observables\": [[observable_classification, observable_name]],\n                \"playbook_requested\": playbook_requested,\n                \"tags_labels\": tags_labels,\n                \"runtime_configuration\": runtime_configuration,\n            }\n            # send this value only if populated,\n            # otherwise the backend would give you 400\n            if tlp:\n                data[\"tlp\"] = tlp\n            answer = self.__send_analysis_request(\n                data=data, files=None, playbook_mode=True\n            )\n        except Exception as e:\n            raise IntelOwlClientException(e)\n        return answer\n\n    def send_analysis_batch(self, rows: List[Dict]):\n        \"\"\"\n        Send multiple analysis requests.\n        Can be mix of observable or file analysis requests.\n\n        Used by the pyintelowl CLI.\n\n        Args:\n            rows (List[Dict]):\n                Each row should be a dictionary with keys,\n                `value`, `type`, `check`, `tlp`,\n                `analyzers_list`, `connectors_list`, `runtime_config`\n                `tags_list`.\n        \"\"\"\n        for obj in rows:\n            try:\n                runtime_config = obj.get(\"runtime_config\", {})\n                if runtime_config:\n                    with open(runtime_config) as fp:\n                        runtime_config = json.load(fp)\n\n                analyzers_list = obj.get(\"analyzers_list\", [])\n                connectors_list = obj.get(\"connectors_list\", [])\n                if isinstance(analyzers_list, str):\n                    analyzers_list = analyzers_list.split(\",\")\n                if isinstance(connectors_list, str):\n                    connectors_list = connectors_list.split(\",\")\n\n                self._new_analysis_cli(\n                    obj[\"value\"],\n                    obj[\"type\"],\n                    obj.get(\"check\", None),\n                    obj.get(\"tlp\", \"WHITE\"),\n                    analyzers_list,\n                    connectors_list,\n                    runtime_config,\n                    obj.get(\"tags_list\", []),\n                    obj.get(\"should_poll\", False),\n                )\n            except IntelOwlClientException as e:\n                self.logger.fatal(str(e))\n\n    def __send_analysis_request(self, data=None, files=None, playbook_mode=False):\n        \"\"\"\n        Internal use only.\n        \"\"\"\n        response = None\n        answer = {}\n        if files is None:\n            url = self.instance + \"/api/analyze_observable\"\n            if playbook_mode:\n                url = self.instance + \"/api/playbook/analyze_multiple_observables\"\n            args = {\"json\": data}\n        else:\n            url = self.instance + \"/api/analyze_file\"\n            if playbook_mode:\n                url = self.instance + \"/api/playbook/analyze_multiple_files\"\n            args = {\"data\": data, \"files\": files}\n        try:\n            response = self.session.post(url, **args)\n            self.logger.debug(\n                msg={\n                    \"url\": response.url,\n                    \"code\": response.status_code,\n                    \"request\": response.request.headers,\n                    \"headers\": response.headers,\n                    \"body\": response.json(),\n                }\n            )\n            answer = response.json()\n            if playbook_mode:\n                # right now, we are only supporting single input result\n                answers = answer.get(\"results\", [])\n                if answers:\n                    answer = answers[0]\n\n            warnings = answer.get(\"warnings\", [])\n            errors = answer.get(\"errors\", {})\n            if self.cli:\n                info_log = f\"\"\"New Job running..\n                    ID: {answer.get('job_id')} | \n                    Status: [u blue]{answer.get('status')}[/].\n                    Got {len(warnings)} warnings:\n                    [i yellow]{warnings if warnings else None}[/]\n                    Got {len(errors)} errors:\n                    [i red]{errors if errors else None}[/]\n                \"\"\"\n            else:\n                info_log = (\n                    f\"New Job running.. ID: {answer.get('job_id')} \"\n                    f\"| Status: {answer.get('status')}.\"\n                    f\" Got {len(warnings)} warnings:\"\n                    f\" {warnings if warnings else None}\"\n                    f\" Got {len(errors)} errors:\"\n                    f\" {errors if errors else None}\"\n                )\n            self.logger.info(info_log)\n            response.raise_for_status()\n        except Exception as e:\n            raise IntelOwlClientException(e, response=response)\n        return answer\n\n    def create_tag(self, label: str, color: str):\n        \"\"\"Creates new tag by sending a POST Request\n        Endpoint: ``/api/tags``\n\n        Args:\n            label ([str]): [Label of the tag to be created]\n            color ([str]): [Color of the tag to be created]\n        \"\"\"\n        url = self.instance + \"/api/tags\"\n        data = {\"label\": label, \"color\": color}\n        response = self.__make_request(\"POST\", url=url, data=data)\n        return response.json()\n\n    def edit_tag(self, tag_id: Union[int, str], label: str, color: str):\n        \"\"\"Edits existing tag by sending PUT request\n        Endpoint: ``api/tags``\n\n        Args:\n            id ([int]): [Id of the existing tag]\n            label ([str]): [Label of the tag to be created]\n            color ([str]): [Color of the tag to be created]\n        \"\"\"\n        url = self.instance + \"/api/tags/\" + str(tag_id)\n        data = {\"label\": label, \"color\": color}\n        response = self.__make_request(\"PUT\", url=url, data=data)\n        return response.json()\n\n    def get_all_tags(self) -> List[Dict[str, str]]:\n        \"\"\"\n        Fetch list of all tags.\\n\n        Endpoint: ``/api/tags``\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            List[Dict[str, str]]: List of tags\n        \"\"\"\n        url = self.instance + \"/api/tags\"\n        response = self.__make_request(\"GET\", url=url)\n        return response.json()\n\n    def get_all_jobs(self) -> List[Dict[str, Any]]:\n        \"\"\"\n        Fetch list of all jobs.\\n\n        Endpoint: ``/api/jobs``\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Dict: Dict with 3 keys: \"count\", \"total_pages\", \"results\"\n        \"\"\"\n        url = self.instance + \"/api/jobs\"\n        response = self.__make_request(\"GET\", url=url)\n        return response.json()\n\n    def get_tag_by_id(self, tag_id: Union[int, str]) -> Dict[str, str]:\n        \"\"\"Fetch tag info by ID.\\n\n        Endpoint: ``/api/tag/{tag_id}``\n\n        Args:\n            tag_id (Union[int, str]): Tag ID\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Dict[str, str]: Dict with 3 keys: `id`, `label` and `color`.\n        \"\"\"\n\n        url = self.instance + \"/api/tags/\" + str(tag_id)\n        response = self.__make_request(\"GET\", url=url)\n        return response.json()\n\n    def get_job_by_id(self, job_id: Union[int, str]) -> Dict[str, Any]:\n        \"\"\"Fetch job info by ID.\n        Endpoint: ``/api/jobs/{job_id}``\n\n        Args:\n            job_id (Union[int, str]): Job ID\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Dict[str, Any]: JSON body.\n        \"\"\"\n        url = self.instance + \"/api/jobs/\" + str(job_id)\n        response = self.__make_request(\"GET\", url=url)\n        return response.json()\n\n    @staticmethod\n    def get_md5(\n        to_hash: AnyStr,\n        type_=\"observable\",\n    ) -> str:\n        \"\"\"Returns md5sum of given observable or file object.\n\n        Args:\n            to_hash (AnyStr):\n                either an observable string, file contents as bytes or path to a file\n            type_ (Union[\"observable\", \"binary\", \"file\"], optional):\n                `observable`, `binary`, `file`. Defaults to \"observable\".\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            str: md5sum\n        \"\"\"\n        md5 = \"\"\n        if type_ == \"observable\":\n            md5 = hashlib.md5(str(to_hash).lower().encode(\"utf-8\")).hexdigest()\n        elif type_ == \"binary\":\n            md5 = hashlib.md5(to_hash).hexdigest()\n        elif type_ == \"file\":\n            path = pathlib.Path(to_hash)\n            if not path.exists():\n                raise IntelOwlClientException(f\"{to_hash} does not exists\")\n            binary = path.read_bytes()\n            md5 = hashlib.md5(binary).hexdigest()\n        return md5\n\n    def _new_analysis_cli(\n        self,\n        obj: str,\n        type_: str,\n        check,\n        tlp: TLPType = None,\n        analyzers_list: List[str] = None,\n        connectors_list: List[str] = None,\n        runtime_configuration: Dict = None,\n        tags_labels: List[str] = None,\n        should_poll: bool = False,\n        minutes_ago: int = None,\n    ) -> None:\n        \"\"\"\n        For internal use by the pyintelowl CLI.\n        \"\"\"\n        if not analyzers_list:\n            analyzers_list = []\n        if not connectors_list:\n            connectors_list = []\n        if not runtime_configuration:\n            runtime_configuration = {}\n        if not tags_labels:\n            tags_labels = []\n        self.logger.info(\n            f\"\"\"Requesting analysis..\n            {type_}: [blue]{obj}[/]\n            analyzers: [i green]{analyzers_list if analyzers_list else 'none'}[/]\n            connectors: [i green]{connectors_list if connectors_list else 'none'}[/]\n            tags: [i green]{tags_labels}[/]\n            \"\"\"\n        )\n        # 1st step: ask analysis availability\n        if check != \"force-new\":\n            md5 = self.get_md5(obj, type_=type_)\n\n            resp = self.ask_analysis_availability(\n                md5,\n                analyzers_list,\n                True if check == \"reported\" else False,\n                minutes_ago,\n            )\n            status, job_id = resp.get(\"status\", None), resp.get(\"job_id\", None)\n            if status != \"not_available\":\n                self.logger.info(\n                    f\"\"\"Found existing analysis!\n                Job: #{job_id}\n                status: [u blue]{status}[/]\n\n                [i]Hint: use [#854442]--check force-new[/] to perform new scan anyway[/]\n                    \"\"\"\n                )\n                return\n        # 2nd step: send new analysis request\n        if type_ == \"observable\":\n            resp2 = self.send_observable_analysis_request(\n                observable_name=obj,\n                tlp=tlp,\n                analyzers_requested=analyzers_list,\n                connectors_requested=connectors_list,\n                runtime_configuration=runtime_configuration,\n                tags_labels=tags_labels,\n            )\n        else:\n            path = pathlib.Path(obj)\n            resp2 = self.send_file_analysis_request(\n                filename=path.name,\n                binary=path.read_bytes(),\n                tlp=tlp,\n                analyzers_requested=analyzers_list,\n                connectors_requested=connectors_list,\n                runtime_configuration=runtime_configuration,\n                tags_labels=tags_labels,\n            )\n        # 3rd step: poll for result\n        if should_poll:\n            if resp2[\"status\"] != \"accepted\":\n                self.logger.fatal(\"Can't poll a failed job\")\n            # import poll function\n            from .cli._jobs_utils import _poll_for_job_cli\n\n            job_id = resp2[\"job_id\"]\n            _ = _poll_for_job_cli(self, job_id)\n            self.logger.info(\n                f\"\"\"\n        Polling finished.\n        Execute [i blue]pyintelowl jobs view {job_id}[/] to view the result\n                \"\"\"\n            )\n\n    def _new_analysis_playbook_cli(\n        self,\n        obj: str,\n        type_: str,\n        playbook: str,\n        tlp: TLPType = None,\n        runtime_configuration: Dict = None,\n        tags_labels: List[str] = None,\n        should_poll: bool = False,\n    ) -> None:\n        \"\"\"\n        For internal use by the pyintelowl CLI.\n        \"\"\"\n        if not runtime_configuration:\n            runtime_configuration = {}\n        if not tags_labels:\n            tags_labels = []\n\n        self.logger.info(\n            f\"\"\"Requesting analysis..\n            {type_}: [blue]{obj}[/]\n            playbook: [i green]{playbook}[/]\n            tags: [i green]{tags_labels}[/]\n            \"\"\"\n        )\n\n        # 1st step, make request\n        if type_ == \"observable\":\n            resp = self.send_observable_analysis_playbook_request(\n                observable_name=obj,\n                playbook_requested=playbook,\n                tlp=tlp,\n                runtime_configuration=runtime_configuration,\n                tags_labels=tags_labels,\n            )\n        else:\n            path = pathlib.Path(obj)\n            resp = self.send_file_analysis_playbook_request(\n                filename=path.name,\n                binary=path.read_bytes(),\n                playbook_requested=playbook,\n                tlp=tlp,\n                runtime_configuration=runtime_configuration,\n                tags_labels=tags_labels,\n            )\n\n        # 2nd step: poll for result\n        if should_poll:\n            if resp.get(\"status\", \"\") != \"accepted\":\n                self.logger.fatal(\"Can't poll a failed job\")\n            # import poll function\n            from .cli._jobs_utils import _poll_for_job_cli\n\n            job_id = resp.get(\"job_id\", 0)\n            _ = _poll_for_job_cli(self, job_id)\n            self.logger.info(\n                f\"\"\"\n                    Polling finished.\n                    Execute [i blue]pyintelowl jobs view {job_id}[/] to view the result\n                \"\"\"\n            )\n\n    def _get_observable_classification(self, value: str) -> str:\n        \"\"\"Returns observable classification for the given value.\\n\n        Only following types are supported:\n        ip, domain, url, hash (md5, sha1, sha256), generic (if no match)\n\n        Args:\n            value (str):\n                observable value\n\n        Raises:\n            IntelOwlClientException:\n                if value type is not recognized\n\n        Returns:\n            str: one of `ip`, `url`, `domain`, `hash` or 'generic'.\n        \"\"\"\n        try:\n            ipaddress.ip_address(value)\n        except ValueError:\n            if re.match(\n                r\"^(?:htt|ft|tc)ps?://[a-z\\d-]{1,63}(?:\\.[a-z\\d-]{1,63})+\"\n                r\"(?:/[a-z\\d-]{1,63})*(?:\\.\\w+)?\",\n                value,\n            ):\n                classification = \"url\"\n            elif re.match(r\"^(\\.)?[a-z\\d-]{1,63}(\\.[a-z\\d-]{1,63})+$\", value):\n                classification = \"domain\"\n            elif (\n                re.match(r\"^[a-f\\d]{32}$\", value)\n                or re.match(r\"^[a-f\\d]{40}$\", value)\n                or re.match(r\"^[a-f\\d]{64}$\", value)\n                or re.match(r\"^[A-F\\d]{32}$\", value)\n                or re.match(r\"^[A-F\\d]{40}$\", value)\n                or re.match(r\"^[A-F\\d]{64}$\", value)\n            ):\n                classification = \"hash\"\n            else:\n                classification = \"generic\"\n                self.logger.warning(\n                    \"Couldn't detect observable classification, setting as 'generic'...\"\n                )\n        else:\n            # its a simple IP\n            classification = \"ip\"\n\n        return classification\n\n    def download_sample(self, job_id: int) -> bytes:\n        \"\"\"\n        Download file sample from job.\\n\n        Method: GET\n        Endpoint: ``/api/jobs/{job_id}/download_sample``\n\n        Args:\n            job_id (int):\n                id of job to download sample from\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bytes: Raw file data.\n        \"\"\"\n\n        url = self.instance + f\"/api/jobs/{job_id}/download_sample\"\n        response = self.__make_request(\"GET\", url=url)\n        return response.content\n\n    def kill_running_job(self, job_id: int) -> bool:\n        \"\"\"Send kill_running_job request.\\n\n        Method: PATCH\n        Endpoint: ``/api/jobs/{job_id}/kill``\n\n        Args:\n            job_id (int):\n                id of job to kill\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: killed or not\n        \"\"\"\n\n        url = self.instance + f\"/api/jobs/{job_id}/kill\"\n        response = self.__make_request(\"PATCH\", url=url)\n        killed = response.status_code == 204\n        return killed\n\n    def delete_job_by_id(self, job_id: int) -> bool:\n        \"\"\"Send delete job request.\\n\n        Method: DELETE\n        Endpoint: ``/api/jobs/{job_id}``\n\n        Args:\n            job_id (int):\n                id of job to kill\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: deleted or not\n        \"\"\"\n        url = self.instance + \"/api/jobs/\" + str(job_id)\n        response = self.__make_request(\"DELETE\", url=url)\n        deleted = response.status_code == 204\n        return deleted\n\n    def delete_tag_by_id(self, tag_id: int) -> bool:\n        \"\"\"Send delete tag request.\\n\n        Method: DELETE\n        Endpoint: ``/api/tags/{tag_id}``\n\n        Args:\n            tag_id (int):\n                id of tag to delete\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: deleted or not\n        \"\"\"\n\n        url = self.instance + \"/api/tags/\" + str(tag_id)\n        response = self.__make_request(\"DELETE\", url=url)\n        deleted = response.status_code == 204\n        return deleted\n\n    def __run_plugin_action(\n        self, job_id: int, plugin_type: str, plugin_name: str, plugin_action: str\n    ) -> bool:\n        \"\"\"Internal method for kill/retry for analyzer/connector\"\"\"\n        response = None\n        url = (\n            self.instance\n            + f\"/api/jobs/{job_id}/{plugin_type}/{plugin_name}/{plugin_action}\"\n        )\n        response = self.__make_request(\"PATCH\", url=url)\n        success = response.status_code == 204\n        return success\n\n    def kill_analyzer(self, job_id: int, analyzer_name: str) -> bool:\n        \"\"\"Send kill running/pending analyzer request.\\n\n        Method: PATCH\n        Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/kill``\n\n        Args:\n            job_id (int):\n                id of job\n            analyzer_name (str):\n                name of analyzer to kill\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: killed or not\n        \"\"\"\n\n        killed = self.__run_plugin_action(\n            job_id=job_id,\n            plugin_name=analyzer_name,\n            plugin_type=\"analyzer\",\n            plugin_action=\"kill\",\n        )\n        return killed\n\n    def kill_connector(self, job_id: int, connector_name: str) -> bool:\n        \"\"\"Send kill running/pending connector request.\\n\n        Method: PATCH\n        Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/kill``\n\n        Args:\n            job_id (int):\n                id of job\n            connector_name (str):\n                name of connector to kill\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: killed or not\n        \"\"\"\n\n        killed = self.__run_plugin_action(\n            job_id=job_id,\n            plugin_name=connector_name,\n            plugin_type=\"connector\",\n            plugin_action=\"kill\",\n        )\n        return killed\n\n    def retry_analyzer(self, job_id: int, analyzer_name: str) -> bool:\n        \"\"\"Send retry failed/killed analyzer request.\\n\n        Method: PATCH\n        Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/retry``\n\n        Args:\n            job_id (int):\n                id of job\n            analyzer_name (str):\n                name of analyzer to retry\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: success or not\n        \"\"\"\n\n        success = self.__run_plugin_action(\n            job_id=job_id,\n            plugin_name=analyzer_name,\n            plugin_type=\"analyzer\",\n            plugin_action=\"retry\",\n        )\n        return success\n\n    def retry_connector(self, job_id: int, connector_name: str) -> bool:\n        \"\"\"Send retry failed/killed connector request.\\n\n        Method: PATCH\n        Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/retry``\n\n        Args:\n            job_id (int):\n                id of job\n            connector_name (str):\n                name of connector to retry\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: success or not\n        \"\"\"\n\n        success = self.__run_plugin_action(\n            job_id=job_id,\n            plugin_name=connector_name,\n            plugin_type=\"connector\",\n            plugin_action=\"retry\",\n        )\n        return success\n\n    def analyzer_healthcheck(self, analyzer_name: str) -> Optional[bool]:\n        \"\"\"Send analyzer(docker-based) health check request.\\n\n        Method: GET\n        Endpoint: ``/api/analyzer/{analyzer_name}/healthcheck``\n\n        Args:\n            analyzer_name (str):\n                name of analyzer\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: success or not\n        \"\"\"\n\n        url = self.instance + f\"/api/analyzer/{analyzer_name}/healthcheck\"\n        response = self.__make_request(\"GET\", url=url)\n        return response.json().get(\"status\", None)\n\n    def connector_healthcheck(self, connector_name: str) -> Optional[bool]:\n        \"\"\"Send connector health check request.\\n\n        Method: GET\n        Endpoint: ``/api/connector/{connector_name}/healthcheck``\n\n        Args:\n            connector_name (str):\n                name of connector\n\n        Raises:\n            IntelOwlClientException: on client/HTTP error\n\n        Returns:\n            Bool: success or not\n        \"\"\"\n        url = self.instance + f\"/api/connector/{connector_name}/healthcheck\"\n        response = self.__make_request(\"GET\", url=url)\n        return response.json().get(\"status\", None)\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.session","title":"session: requests.Session property","text":"

                          Internal use only.

                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.__make_request","title":"__make_request(method='GET', *args, **kwargs)","text":"

                          For internal use only.

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def __make_request(\n    self,\n    method: Literal[\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"] = \"GET\",\n    *args,\n    **kwargs,\n) -> requests.Response:\n    \"\"\"\n    For internal use only.\n    \"\"\"\n    response: requests.Response = None\n    requests_function_map: Dict[str, Callable] = {\n        \"GET\": self.session.get,\n        \"POST\": self.session.post,\n        \"PUT\": self.session.put,\n        \"PATCH\": self.session.patch,\n        \"DELETE\": self.session.delete,\n    }\n    func = requests_function_map.get(method, None)\n    if not func:\n        raise RuntimeError(f\"Unsupported method name: {method}\")\n\n    try:\n        response = func(*args, **kwargs)\n        self.logger.debug(\n            msg=(response.url, response.status_code, response.content)\n        )\n        response.raise_for_status()\n    except Exception as e:\n        raise IntelOwlClientException(e, response=response)\n\n    return response\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.__run_plugin_action","title":"__run_plugin_action(job_id, plugin_type, plugin_name, plugin_action)","text":"

                          Internal method for kill/retry for analyzer/connector

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def __run_plugin_action(\n    self, job_id: int, plugin_type: str, plugin_name: str, plugin_action: str\n) -> bool:\n    \"\"\"Internal method for kill/retry for analyzer/connector\"\"\"\n    response = None\n    url = (\n        self.instance\n        + f\"/api/jobs/{job_id}/{plugin_type}/{plugin_name}/{plugin_action}\"\n    )\n    response = self.__make_request(\"PATCH\", url=url)\n    success = response.status_code == 204\n    return success\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.__send_analysis_request","title":"__send_analysis_request(data=None, files=None, playbook_mode=False)","text":"

                          Internal use only.

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def __send_analysis_request(self, data=None, files=None, playbook_mode=False):\n    \"\"\"\n    Internal use only.\n    \"\"\"\n    response = None\n    answer = {}\n    if files is None:\n        url = self.instance + \"/api/analyze_observable\"\n        if playbook_mode:\n            url = self.instance + \"/api/playbook/analyze_multiple_observables\"\n        args = {\"json\": data}\n    else:\n        url = self.instance + \"/api/analyze_file\"\n        if playbook_mode:\n            url = self.instance + \"/api/playbook/analyze_multiple_files\"\n        args = {\"data\": data, \"files\": files}\n    try:\n        response = self.session.post(url, **args)\n        self.logger.debug(\n            msg={\n                \"url\": response.url,\n                \"code\": response.status_code,\n                \"request\": response.request.headers,\n                \"headers\": response.headers,\n                \"body\": response.json(),\n            }\n        )\n        answer = response.json()\n        if playbook_mode:\n            # right now, we are only supporting single input result\n            answers = answer.get(\"results\", [])\n            if answers:\n                answer = answers[0]\n\n        warnings = answer.get(\"warnings\", [])\n        errors = answer.get(\"errors\", {})\n        if self.cli:\n            info_log = f\"\"\"New Job running..\n                ID: {answer.get('job_id')} | \n                Status: [u blue]{answer.get('status')}[/].\n                Got {len(warnings)} warnings:\n                [i yellow]{warnings if warnings else None}[/]\n                Got {len(errors)} errors:\n                [i red]{errors if errors else None}[/]\n            \"\"\"\n        else:\n            info_log = (\n                f\"New Job running.. ID: {answer.get('job_id')} \"\n                f\"| Status: {answer.get('status')}.\"\n                f\" Got {len(warnings)} warnings:\"\n                f\" {warnings if warnings else None}\"\n                f\" Got {len(errors)} errors:\"\n                f\" {errors if errors else None}\"\n            )\n        self.logger.info(info_log)\n        response.raise_for_status()\n    except Exception as e:\n        raise IntelOwlClientException(e, response=response)\n    return answer\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.analyzer_healthcheck","title":"analyzer_healthcheck(analyzer_name)","text":"

                          Send analyzer(docker-based) health check request.

                          Method: GET Endpoint: /api/analyzer/{analyzer_name}/healthcheck

                          Parameters:

                          Name Type Description Default analyzer_name str

                          name of analyzer

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool Optional[bool]

                          success or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def analyzer_healthcheck(self, analyzer_name: str) -> Optional[bool]:\n    \"\"\"Send analyzer(docker-based) health check request.\\n\n    Method: GET\n    Endpoint: ``/api/analyzer/{analyzer_name}/healthcheck``\n\n    Args:\n        analyzer_name (str):\n            name of analyzer\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: success or not\n    \"\"\"\n\n    url = self.instance + f\"/api/analyzer/{analyzer_name}/healthcheck\"\n    response = self.__make_request(\"GET\", url=url)\n    return response.json().get(\"status\", None)\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.ask_analysis_availability","title":"ask_analysis_availability(md5, analyzers=None, check_reported_analysis_too=False, minutes_ago=None)","text":"

                          Search for already available analysis.

                          Endpoint: /api/ask_analysis_availability

                          Parameters:

                          Name Type Description Default md5 str

                          md5sum of the observable or file

                          required analyzers List[str] None check_reported_analysis_too bool False minutes_ago int None

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Dict Dict

                          JSON body

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def ask_analysis_availability(\n    self,\n    md5: str,\n    analyzers: List[str] = None,\n    check_reported_analysis_too: bool = False,\n    minutes_ago: int = None,\n) -> Dict:\n    \"\"\"Search for already available analysis.\\n\n    Endpoint: ``/api/ask_analysis_availability``\n\n    Args:\n        md5 (str): md5sum of the observable or file\n        analyzers (List[str], optional):\n        list of analyzers to trigger.\n        Defaults to `None` meaning automatically select all configured analyzers.\n        check_reported_analysis_too (bool, optional):\n        Check against all existing jobs. Defaults to ``False``.\n        minutes_ago (int, optional):\n        number of minutes to check back for analysis.\n        Default is None so the check does not have any time limits.\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Dict: JSON body\n    \"\"\"\n    if not analyzers:\n        analyzers = []\n    data = {\"md5\": md5, \"analyzers\": analyzers}\n    if not check_reported_analysis_too:\n        data[\"running_only\"] = True\n    if minutes_ago:\n        data[\"minutes_ago\"] = int(minutes_ago)\n    url = self.instance + \"/api/ask_analysis_availability\"\n    response = self.__make_request(\"POST\", url=url, data=data)\n    answer = response.json()\n    status, job_id = answer.get(\"status\", None), answer.get(\"job_id\", None)\n    # check sanity cases\n    if not status:\n        raise IntelOwlClientException(\n            \"API ask_analysis_availability gave result without status ?\"\n            f\" Response: {answer}\"\n        )\n    if status != \"not_available\" and not job_id:\n        raise IntelOwlClientException(\n            \"API ask_analysis_availability gave result without job_id ?\"\n            f\" Response: {answer}\"\n        )\n    return answer\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.connector_healthcheck","title":"connector_healthcheck(connector_name)","text":"

                          Send connector health check request.

                          Method: GET Endpoint: /api/connector/{connector_name}/healthcheck

                          Parameters:

                          Name Type Description Default connector_name str

                          name of connector

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool Optional[bool]

                          success or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def connector_healthcheck(self, connector_name: str) -> Optional[bool]:\n    \"\"\"Send connector health check request.\\n\n    Method: GET\n    Endpoint: ``/api/connector/{connector_name}/healthcheck``\n\n    Args:\n        connector_name (str):\n            name of connector\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: success or not\n    \"\"\"\n    url = self.instance + f\"/api/connector/{connector_name}/healthcheck\"\n    response = self.__make_request(\"GET\", url=url)\n    return response.json().get(\"status\", None)\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.create_tag","title":"create_tag(label, color)","text":"

                          Creates new tag by sending a POST Request Endpoint: /api/tags

                          Parameters:

                          Name Type Description Default label [str]

                          [Label of the tag to be created]

                          required color [str]

                          [Color of the tag to be created]

                          required Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def create_tag(self, label: str, color: str):\n    \"\"\"Creates new tag by sending a POST Request\n    Endpoint: ``/api/tags``\n\n    Args:\n        label ([str]): [Label of the tag to be created]\n        color ([str]): [Color of the tag to be created]\n    \"\"\"\n    url = self.instance + \"/api/tags\"\n    data = {\"label\": label, \"color\": color}\n    response = self.__make_request(\"POST\", url=url, data=data)\n    return response.json()\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.delete_job_by_id","title":"delete_job_by_id(job_id)","text":"

                          Send delete job request.

                          Method: DELETE Endpoint: /api/jobs/{job_id}

                          Parameters:

                          Name Type Description Default job_id int

                          id of job to kill

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          deleted or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def delete_job_by_id(self, job_id: int) -> bool:\n    \"\"\"Send delete job request.\\n\n    Method: DELETE\n    Endpoint: ``/api/jobs/{job_id}``\n\n    Args:\n        job_id (int):\n            id of job to kill\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: deleted or not\n    \"\"\"\n    url = self.instance + \"/api/jobs/\" + str(job_id)\n    response = self.__make_request(\"DELETE\", url=url)\n    deleted = response.status_code == 204\n    return deleted\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.delete_tag_by_id","title":"delete_tag_by_id(tag_id)","text":"

                          Send delete tag request.

                          Method: DELETE Endpoint: /api/tags/{tag_id}

                          Parameters:

                          Name Type Description Default tag_id int

                          id of tag to delete

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          deleted or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def delete_tag_by_id(self, tag_id: int) -> bool:\n    \"\"\"Send delete tag request.\\n\n    Method: DELETE\n    Endpoint: ``/api/tags/{tag_id}``\n\n    Args:\n        tag_id (int):\n            id of tag to delete\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: deleted or not\n    \"\"\"\n\n    url = self.instance + \"/api/tags/\" + str(tag_id)\n    response = self.__make_request(\"DELETE\", url=url)\n    deleted = response.status_code == 204\n    return deleted\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.download_sample","title":"download_sample(job_id)","text":"

                          Download file sample from job.

                          Method: GET Endpoint: /api/jobs/{job_id}/download_sample

                          Parameters:

                          Name Type Description Default job_id int

                          id of job to download sample from

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bytes bytes

                          Raw file data.

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def download_sample(self, job_id: int) -> bytes:\n    \"\"\"\n    Download file sample from job.\\n\n    Method: GET\n    Endpoint: ``/api/jobs/{job_id}/download_sample``\n\n    Args:\n        job_id (int):\n            id of job to download sample from\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bytes: Raw file data.\n    \"\"\"\n\n    url = self.instance + f\"/api/jobs/{job_id}/download_sample\"\n    response = self.__make_request(\"GET\", url=url)\n    return response.content\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.edit_tag","title":"edit_tag(tag_id, label, color)","text":"

                          Edits existing tag by sending PUT request Endpoint: api/tags

                          Parameters:

                          Name Type Description Default id [int]

                          [Id of the existing tag]

                          required label [str]

                          [Label of the tag to be created]

                          required color [str]

                          [Color of the tag to be created]

                          required Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def edit_tag(self, tag_id: Union[int, str], label: str, color: str):\n    \"\"\"Edits existing tag by sending PUT request\n    Endpoint: ``api/tags``\n\n    Args:\n        id ([int]): [Id of the existing tag]\n        label ([str]): [Label of the tag to be created]\n        color ([str]): [Color of the tag to be created]\n    \"\"\"\n    url = self.instance + \"/api/tags/\" + str(tag_id)\n    data = {\"label\": label, \"color\": color}\n    response = self.__make_request(\"PUT\", url=url, data=data)\n    return response.json()\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.get_all_jobs","title":"get_all_jobs()","text":"

                          Fetch list of all jobs.

                          Endpoint: /api/jobs

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Dict List[Dict[str, Any]]

                          Dict with 3 keys: \"count\", \"total_pages\", \"results\"

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def get_all_jobs(self) -> List[Dict[str, Any]]:\n    \"\"\"\n    Fetch list of all jobs.\\n\n    Endpoint: ``/api/jobs``\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Dict: Dict with 3 keys: \"count\", \"total_pages\", \"results\"\n    \"\"\"\n    url = self.instance + \"/api/jobs\"\n    response = self.__make_request(\"GET\", url=url)\n    return response.json()\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.get_all_tags","title":"get_all_tags()","text":"

                          Fetch list of all tags.

                          Endpoint: /api/tags

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Type Description List[Dict[str, str]]

                          List[Dict[str, str]]: List of tags

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def get_all_tags(self) -> List[Dict[str, str]]:\n    \"\"\"\n    Fetch list of all tags.\\n\n    Endpoint: ``/api/tags``\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        List[Dict[str, str]]: List of tags\n    \"\"\"\n    url = self.instance + \"/api/tags\"\n    response = self.__make_request(\"GET\", url=url)\n    return response.json()\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.get_job_by_id","title":"get_job_by_id(job_id)","text":"

                          Fetch job info by ID. Endpoint: /api/jobs/{job_id}

                          Parameters:

                          Name Type Description Default job_id Union[int, str]

                          Job ID

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Type Description Dict[str, Any]

                          Dict[str, Any]: JSON body.

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def get_job_by_id(self, job_id: Union[int, str]) -> Dict[str, Any]:\n    \"\"\"Fetch job info by ID.\n    Endpoint: ``/api/jobs/{job_id}``\n\n    Args:\n        job_id (Union[int, str]): Job ID\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Dict[str, Any]: JSON body.\n    \"\"\"\n    url = self.instance + \"/api/jobs/\" + str(job_id)\n    response = self.__make_request(\"GET\", url=url)\n    return response.json()\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.get_md5","title":"get_md5(to_hash, type_='observable') staticmethod","text":"

                          Returns md5sum of given observable or file object.

                          Parameters:

                          Name Type Description Default to_hash AnyStr

                          either an observable string, file contents as bytes or path to a file

                          required type_ Union[observable, binary, file]

                          observable, binary, file. Defaults to \"observable\".

                          'observable'

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description str str

                          md5sum

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          @staticmethod\ndef get_md5(\n    to_hash: AnyStr,\n    type_=\"observable\",\n) -> str:\n    \"\"\"Returns md5sum of given observable or file object.\n\n    Args:\n        to_hash (AnyStr):\n            either an observable string, file contents as bytes or path to a file\n        type_ (Union[\"observable\", \"binary\", \"file\"], optional):\n            `observable`, `binary`, `file`. Defaults to \"observable\".\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        str: md5sum\n    \"\"\"\n    md5 = \"\"\n    if type_ == \"observable\":\n        md5 = hashlib.md5(str(to_hash).lower().encode(\"utf-8\")).hexdigest()\n    elif type_ == \"binary\":\n        md5 = hashlib.md5(to_hash).hexdigest()\n    elif type_ == \"file\":\n        path = pathlib.Path(to_hash)\n        if not path.exists():\n            raise IntelOwlClientException(f\"{to_hash} does not exists\")\n        binary = path.read_bytes()\n        md5 = hashlib.md5(binary).hexdigest()\n    return md5\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.get_tag_by_id","title":"get_tag_by_id(tag_id)","text":"

                          Fetch tag info by ID.

                          Endpoint: /api/tag/{tag_id}

                          Parameters:

                          Name Type Description Default tag_id Union[int, str]

                          Tag ID

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Type Description Dict[str, str]

                          Dict[str, str]: Dict with 3 keys: id, label and color.

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def get_tag_by_id(self, tag_id: Union[int, str]) -> Dict[str, str]:\n    \"\"\"Fetch tag info by ID.\\n\n    Endpoint: ``/api/tag/{tag_id}``\n\n    Args:\n        tag_id (Union[int, str]): Tag ID\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Dict[str, str]: Dict with 3 keys: `id`, `label` and `color`.\n    \"\"\"\n\n    url = self.instance + \"/api/tags/\" + str(tag_id)\n    response = self.__make_request(\"GET\", url=url)\n    return response.json()\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.kill_analyzer","title":"kill_analyzer(job_id, analyzer_name)","text":"

                          Send kill running/pending analyzer request.

                          Method: PATCH Endpoint: /api/jobs/{job_id}/analyzer/{analyzer_name}/kill

                          Parameters:

                          Name Type Description Default job_id int

                          id of job

                          required analyzer_name str

                          name of analyzer to kill

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          killed or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def kill_analyzer(self, job_id: int, analyzer_name: str) -> bool:\n    \"\"\"Send kill running/pending analyzer request.\\n\n    Method: PATCH\n    Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/kill``\n\n    Args:\n        job_id (int):\n            id of job\n        analyzer_name (str):\n            name of analyzer to kill\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: killed or not\n    \"\"\"\n\n    killed = self.__run_plugin_action(\n        job_id=job_id,\n        plugin_name=analyzer_name,\n        plugin_type=\"analyzer\",\n        plugin_action=\"kill\",\n    )\n    return killed\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.kill_connector","title":"kill_connector(job_id, connector_name)","text":"

                          Send kill running/pending connector request.

                          Method: PATCH Endpoint: /api/jobs/{job_id}/connector/{connector_name}/kill

                          Parameters:

                          Name Type Description Default job_id int

                          id of job

                          required connector_name str

                          name of connector to kill

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          killed or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def kill_connector(self, job_id: int, connector_name: str) -> bool:\n    \"\"\"Send kill running/pending connector request.\\n\n    Method: PATCH\n    Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/kill``\n\n    Args:\n        job_id (int):\n            id of job\n        connector_name (str):\n            name of connector to kill\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: killed or not\n    \"\"\"\n\n    killed = self.__run_plugin_action(\n        job_id=job_id,\n        plugin_name=connector_name,\n        plugin_type=\"connector\",\n        plugin_action=\"kill\",\n    )\n    return killed\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.kill_running_job","title":"kill_running_job(job_id)","text":"

                          Send kill_running_job request.

                          Method: PATCH Endpoint: /api/jobs/{job_id}/kill

                          Parameters:

                          Name Type Description Default job_id int

                          id of job to kill

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          killed or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def kill_running_job(self, job_id: int) -> bool:\n    \"\"\"Send kill_running_job request.\\n\n    Method: PATCH\n    Endpoint: ``/api/jobs/{job_id}/kill``\n\n    Args:\n        job_id (int):\n            id of job to kill\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: killed or not\n    \"\"\"\n\n    url = self.instance + f\"/api/jobs/{job_id}/kill\"\n    response = self.__make_request(\"PATCH\", url=url)\n    killed = response.status_code == 204\n    return killed\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.retry_analyzer","title":"retry_analyzer(job_id, analyzer_name)","text":"

                          Send retry failed/killed analyzer request.

                          Method: PATCH Endpoint: /api/jobs/{job_id}/analyzer/{analyzer_name}/retry

                          Parameters:

                          Name Type Description Default job_id int

                          id of job

                          required analyzer_name str

                          name of analyzer to retry

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          success or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def retry_analyzer(self, job_id: int, analyzer_name: str) -> bool:\n    \"\"\"Send retry failed/killed analyzer request.\\n\n    Method: PATCH\n    Endpoint: ``/api/jobs/{job_id}/analyzer/{analyzer_name}/retry``\n\n    Args:\n        job_id (int):\n            id of job\n        analyzer_name (str):\n            name of analyzer to retry\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: success or not\n    \"\"\"\n\n    success = self.__run_plugin_action(\n        job_id=job_id,\n        plugin_name=analyzer_name,\n        plugin_type=\"analyzer\",\n        plugin_action=\"retry\",\n    )\n    return success\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.retry_connector","title":"retry_connector(job_id, connector_name)","text":"

                          Send retry failed/killed connector request.

                          Method: PATCH Endpoint: /api/jobs/{job_id}/connector/{connector_name}/retry

                          Parameters:

                          Name Type Description Default job_id int

                          id of job

                          required connector_name str

                          name of connector to retry

                          required

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Bool bool

                          success or not

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def retry_connector(self, job_id: int, connector_name: str) -> bool:\n    \"\"\"Send retry failed/killed connector request.\\n\n    Method: PATCH\n    Endpoint: ``/api/jobs/{job_id}/connector/{connector_name}/retry``\n\n    Args:\n        job_id (int):\n            id of job\n        connector_name (str):\n            name of connector to retry\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Bool: success or not\n    \"\"\"\n\n    success = self.__run_plugin_action(\n        job_id=job_id,\n        plugin_name=connector_name,\n        plugin_type=\"connector\",\n        plugin_action=\"retry\",\n    )\n    return success\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.send_analysis_batch","title":"send_analysis_batch(rows)","text":"

                          Send multiple analysis requests. Can be mix of observable or file analysis requests.

                          Used by the pyintelowl CLI.

                          Parameters:

                          Name Type Description Default rows List[Dict]

                          Each row should be a dictionary with keys, value, type, check, tlp, analyzers_list, connectors_list, runtime_config tags_list.

                          required Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def send_analysis_batch(self, rows: List[Dict]):\n    \"\"\"\n    Send multiple analysis requests.\n    Can be mix of observable or file analysis requests.\n\n    Used by the pyintelowl CLI.\n\n    Args:\n        rows (List[Dict]):\n            Each row should be a dictionary with keys,\n            `value`, `type`, `check`, `tlp`,\n            `analyzers_list`, `connectors_list`, `runtime_config`\n            `tags_list`.\n    \"\"\"\n    for obj in rows:\n        try:\n            runtime_config = obj.get(\"runtime_config\", {})\n            if runtime_config:\n                with open(runtime_config) as fp:\n                    runtime_config = json.load(fp)\n\n            analyzers_list = obj.get(\"analyzers_list\", [])\n            connectors_list = obj.get(\"connectors_list\", [])\n            if isinstance(analyzers_list, str):\n                analyzers_list = analyzers_list.split(\",\")\n            if isinstance(connectors_list, str):\n                connectors_list = connectors_list.split(\",\")\n\n            self._new_analysis_cli(\n                obj[\"value\"],\n                obj[\"type\"],\n                obj.get(\"check\", None),\n                obj.get(\"tlp\", \"WHITE\"),\n                analyzers_list,\n                connectors_list,\n                runtime_config,\n                obj.get(\"tags_list\", []),\n                obj.get(\"should_poll\", False),\n            )\n        except IntelOwlClientException as e:\n            self.logger.fatal(str(e))\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.send_file_analysis_playbook_request","title":"send_file_analysis_playbook_request(filename, binary, playbook_requested, tlp='CLEAR', runtime_configuration=None, tags_labels=None)","text":"

                          Send playbook analysis request for a file.

                          Endpoint: /api/playbook/analyze_multiple_files

                          Args:

                          filename (str):\n    Filename\nbinary (bytes):\n    File contents as bytes\nplaybook_requested (str, optional):\ntlp (str, optional):\n    TLP for the analysis.\n    (options: ``WHITE, GREEN, AMBER, RED``).\nruntime_configuration (Dict, optional):\n    Overwrite configuration for analyzers. Defaults to ``{}``.\ntags_labels (List[str], optional):\n    List of tag labels to assign (creates non-existing tags)\n

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Dict Dict

                          JSON body

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def send_file_analysis_playbook_request(\n    self,\n    filename: str,\n    binary: bytes,\n    playbook_requested: str,\n    tlp: TLPType = \"CLEAR\",\n    runtime_configuration: Dict = None,\n    tags_labels: List[str] = None,\n) -> Dict:\n    \"\"\"Send playbook analysis request for a file.\\n\n    Endpoint: ``/api/playbook/analyze_multiple_files``\n\n    Args:\n\n        filename (str):\n            Filename\n        binary (bytes):\n            File contents as bytes\n        playbook_requested (str, optional):\n        tlp (str, optional):\n            TLP for the analysis.\n            (options: ``WHITE, GREEN, AMBER, RED``).\n        runtime_configuration (Dict, optional):\n            Overwrite configuration for analyzers. Defaults to ``{}``.\n        tags_labels (List[str], optional):\n            List of tag labels to assign (creates non-existing tags)\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Dict: JSON body\n    \"\"\"\n    try:\n        if not tags_labels:\n            tags_labels = []\n        if not runtime_configuration:\n            runtime_configuration = {}\n        data = {\n            \"playbook_requested\": playbook_requested,\n            \"tags_labels\": tags_labels,\n        }\n        # send this value only if populated,\n        # otherwise the backend would give you 400\n        if tlp:\n            data[\"tlp\"] = tlp\n\n        if runtime_configuration:\n            data[\"runtime_configuration\"] = json.dumps(runtime_configuration)\n        # `files` is wanted to be different from the other\n        # /api/analyze_file endpoint\n        # because the server is using different serializers\n        files = {\"files\": (filename, binary)}\n        answer = self.__send_analysis_request(\n            data=data, files=files, playbook_mode=True\n        )\n    except Exception as e:\n        raise IntelOwlClientException(e)\n    return answer\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.send_file_analysis_request","title":"send_file_analysis_request(filename, binary, tlp='CLEAR', analyzers_requested=None, connectors_requested=None, runtime_configuration=None, tags_labels=None)","text":"

                          Send analysis request for a file.

                          Endpoint: /api/analyze_file

                          Args:

                          filename (str):\n    Filename\nbinary (bytes):\n    File contents as bytes\nanalyzers_requested (List[str], optional):\n    List of analyzers to invoke\n    Defaults to ``[]`` i.e. all analyzers.\nconnectors_requested (List[str], optional):\n    List of specific connectors to invoke.\n    Defaults to ``[]`` i.e. all connectors.\ntlp (str, optional):\n    TLP for the analysis.\n    (options: ``CLEAR, GREEN, AMBER, RED``).\nruntime_configuration (Dict, optional):\n    Overwrite configuration for analyzers. Defaults to ``{}``.\ntags_labels (List[str], optional):\n    List of tag labels to assign (creates non-existing tags)\n

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          Returns:

                          Name Type Description Dict Dict

                          JSON body

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def send_file_analysis_request(\n    self,\n    filename: str,\n    binary: bytes,\n    tlp: TLPType = \"CLEAR\",\n    analyzers_requested: List[str] = None,\n    connectors_requested: List[str] = None,\n    runtime_configuration: Dict = None,\n    tags_labels: List[str] = None,\n) -> Dict:\n    \"\"\"Send analysis request for a file.\\n\n    Endpoint: ``/api/analyze_file``\n\n    Args:\n\n        filename (str):\n            Filename\n        binary (bytes):\n            File contents as bytes\n        analyzers_requested (List[str], optional):\n            List of analyzers to invoke\n            Defaults to ``[]`` i.e. all analyzers.\n        connectors_requested (List[str], optional):\n            List of specific connectors to invoke.\n            Defaults to ``[]`` i.e. all connectors.\n        tlp (str, optional):\n            TLP for the analysis.\n            (options: ``CLEAR, GREEN, AMBER, RED``).\n        runtime_configuration (Dict, optional):\n            Overwrite configuration for analyzers. Defaults to ``{}``.\n        tags_labels (List[str], optional):\n            List of tag labels to assign (creates non-existing tags)\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n\n    Returns:\n        Dict: JSON body\n    \"\"\"\n    try:\n        if not tlp:\n            tlp = \"CLEAR\"\n        if not analyzers_requested:\n            analyzers_requested = []\n        if not connectors_requested:\n            connectors_requested = []\n        if not tags_labels:\n            tags_labels = []\n        if not runtime_configuration:\n            runtime_configuration = {}\n        data = {\n            \"file_name\": filename,\n            \"analyzers_requested\": analyzers_requested,\n            \"connectors_requested\": connectors_requested,\n            \"tlp\": tlp,\n            \"tags_labels\": tags_labels,\n        }\n        if runtime_configuration:\n            data[\"runtime_configuration\"] = json.dumps(runtime_configuration)\n        files = {\"file\": (filename, binary)}\n        answer = self.__send_analysis_request(data=data, files=files)\n    except Exception as e:\n        raise IntelOwlClientException(e)\n    return answer\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.send_observable_analysis_playbook_request","title":"send_observable_analysis_playbook_request(observable_name, playbook_requested, tlp='CLEAR', runtime_configuration=None, tags_labels=None, observable_classification=None)","text":"

                          Send playbook analysis request for an observable.

                          Endpoint: /api/playbook/analyze_multiple_observables

                          Parameters:

                          Name Type Description Default observable_name str

                          Observable value

                          required playbook_requested str required tlp str

                          TLP for the analysis. (options: WHITE, GREEN, AMBER, RED).

                          'CLEAR' runtime_configuration Dict

                          Overwrite configuration for analyzers. Defaults to {}.

                          None tags_labels List[str]

                          List of tag labels to assign (creates non-existing tags)

                          None observable_classification str

                          Observable classification, Default to None. By default launch analysis with an automatic classification. (options: url, domain, hash, ip, generic)

                          None

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          IntelOwlClientException

                          on wrong observable_classification

                          Returns:

                          Name Type Description Dict Dict

                          JSON body

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def send_observable_analysis_playbook_request(\n    self,\n    observable_name: str,\n    playbook_requested: str,\n    tlp: TLPType = \"CLEAR\",\n    runtime_configuration: Dict = None,\n    tags_labels: List[str] = None,\n    observable_classification: str = None,\n) -> Dict:\n    \"\"\"Send playbook analysis request for an observable.\\n\n    Endpoint: ``/api/playbook/analyze_multiple_observables``\n\n    Args:\n        observable_name (str):\n            Observable value\n        playbook_requested str:\n        tlp (str, optional):\n            TLP for the analysis.\n            (options: ``WHITE, GREEN, AMBER, RED``).\n        runtime_configuration (Dict, optional):\n            Overwrite configuration for analyzers. Defaults to ``{}``.\n        tags_labels (List[str], optional):\n            List of tag labels to assign (creates non-existing tags)\n        observable_classification (str):\n            Observable classification, Default to None.\n            By default launch analysis with an automatic classification.\n            (options: ``url, domain, hash, ip, generic``)\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n        IntelOwlClientException: on wrong observable_classification\n\n    Returns:\n        Dict: JSON body\n    \"\"\"\n    try:\n        if not tags_labels:\n            tags_labels = []\n        if not runtime_configuration:\n            runtime_configuration = {}\n        if not observable_classification:\n            observable_classification = self._get_observable_classification(\n                observable_name\n            )\n        elif observable_classification not in [\n            \"generic\",\n            \"hash\",\n            \"ip\",\n            \"domain\",\n            \"url\",\n        ]:\n            raise IntelOwlClientException(\n                \"Observable classification only handle\"\n                \" 'generic', 'hash', 'ip', 'domain' and 'url' \"\n            )\n        data = {\n            \"observables\": [[observable_classification, observable_name]],\n            \"playbook_requested\": playbook_requested,\n            \"tags_labels\": tags_labels,\n            \"runtime_configuration\": runtime_configuration,\n        }\n        # send this value only if populated,\n        # otherwise the backend would give you 400\n        if tlp:\n            data[\"tlp\"] = tlp\n        answer = self.__send_analysis_request(\n            data=data, files=None, playbook_mode=True\n        )\n    except Exception as e:\n        raise IntelOwlClientException(e)\n    return answer\n
                          "},{"location":"pyintelowl/IntelOwlClass/#docs.Submodules.pyintelowl.pyintelowl.IntelOwl.send_observable_analysis_request","title":"send_observable_analysis_request(observable_name, tlp='CLEAR', analyzers_requested=None, connectors_requested=None, runtime_configuration=None, tags_labels=None, observable_classification=None)","text":"

                          Send analysis request for an observable.

                          Endpoint: /api/analyze_observable

                          Parameters:

                          Name Type Description Default observable_name str

                          Observable value

                          required analyzers_requested List[str]

                          List of analyzers to invoke Defaults to [] i.e. all analyzers.

                          None connectors_requested List[str]

                          List of specific connectors to invoke. Defaults to [] i.e. all connectors.

                          None tlp str

                          TLP for the analysis. (options: CLEAR, GREEN, AMBER, RED).

                          'CLEAR' runtime_configuration Dict

                          Overwrite configuration for analyzers. Defaults to {}.

                          None tags_labels List[str]

                          List of tag labels to assign (creates non-existing tags)

                          None observable_classification str

                          Observable classification, Default to None. By default launch analysis with an automatic classification. (options: url, domain, hash, ip, generic)

                          None

                          Raises:

                          Type Description IntelOwlClientException

                          on client/HTTP error

                          IntelOwlClientException

                          on wrong observable_classification

                          Returns:

                          Name Type Description Dict Dict

                          JSON body

                          Source code in docs/Submodules/pyintelowl/pyintelowl/pyintelowl.py
                          def send_observable_analysis_request(\n    self,\n    observable_name: str,\n    tlp: TLPType = \"CLEAR\",\n    analyzers_requested: List[str] = None,\n    connectors_requested: List[str] = None,\n    runtime_configuration: Dict = None,\n    tags_labels: List[str] = None,\n    observable_classification: str = None,\n) -> Dict:\n    \"\"\"Send analysis request for an observable.\\n\n    Endpoint: ``/api/analyze_observable``\n\n    Args:\n        observable_name (str):\n            Observable value\n        analyzers_requested (List[str], optional):\n            List of analyzers to invoke\n            Defaults to ``[]`` i.e. all analyzers.\n        connectors_requested (List[str], optional):\n            List of specific connectors to invoke.\n            Defaults to ``[]`` i.e. all connectors.\n        tlp (str, optional):\n            TLP for the analysis.\n            (options: ``CLEAR, GREEN, AMBER, RED``).\n        runtime_configuration (Dict, optional):\n            Overwrite configuration for analyzers. Defaults to ``{}``.\n        tags_labels (List[str], optional):\n            List of tag labels to assign (creates non-existing tags)\n        observable_classification (str):\n            Observable classification, Default to None.\n            By default launch analysis with an automatic classification.\n            (options: ``url, domain, hash, ip, generic``)\n\n    Raises:\n        IntelOwlClientException: on client/HTTP error\n        IntelOwlClientException: on wrong observable_classification\n\n    Returns:\n        Dict: JSON body\n    \"\"\"\n    try:\n        if not tlp:\n            tlp = \"CLEAR\"\n        if not analyzers_requested:\n            analyzers_requested = []\n        if not connectors_requested:\n            connectors_requested = []\n        if not tags_labels:\n            tags_labels = []\n        if not runtime_configuration:\n            runtime_configuration = {}\n        if not observable_classification:\n            observable_classification = self._get_observable_classification(\n                observable_name\n            )\n        elif observable_classification not in [\n            \"generic\",\n            \"hash\",\n            \"ip\",\n            \"domain\",\n            \"url\",\n        ]:\n            raise IntelOwlClientException(\n                \"Observable classification only handle\"\n                \" 'generic', 'hash', 'ip', 'domain' and 'url' \"\n            )\n        data = {\n            \"observable_name\": observable_name,\n            \"observable_classification\": observable_classification,\n            \"analyzers_requested\": analyzers_requested,\n            \"connectors_requested\": connectors_requested,\n            \"tlp\": tlp,\n            \"tags_labels\": tags_labels,\n            \"runtime_configuration\": runtime_configuration,\n        }\n        answer = self.__send_analysis_request(data=data, files=None)\n    except Exception as e:\n        raise IntelOwlClientException(e)\n    return answer\n
                          "},{"location":"pyintelowl/IntelOwlClientException/","title":"IntelOwlClientException","text":""},{"location":"pyintelowl/IntelOwlClientException/#intelowlclientexception-class","title":"IntelOwlClientException Class","text":"

                          Bases: RequestException

                          Source code in docs/Submodules/pyintelowl/pyintelowl/exceptions.py
                          class IntelOwlClientException(RequestException):\n    @property\n    def error_detail(self) -> typing.Union[typing.Dict, typing.AnyStr]:\n        content = None\n        try:\n            content = self.response.json()\n            detail = content.get(\"detail\", None)\n            if detail:\n                content = detail\n        except json.JSONDecodeError:\n            content = self.response.content\n        except Exception:\n            pass\n\n        return content\n\n    def __str__(self):\n        err_msg = super().__str__()\n        detail = self.error_detail\n        return err_msg + f\". Details: {detail}\"\n
                          "},{"location":"pyintelowl/Tests/","title":"Tests","text":""},{"location":"pyintelowl/Tests/#configuration","title":"Configuration","text":"

                          Some tests require file samples, which can be found in the encrypted folder tests/test_files.zip (password: \"infected\"). Unzip the archive in tests/test_files folder before running the tests.

                          Please remember that these are dangerous malware! They come encrypted and locked for a reason! Do NOT run them unless you are absolutely sure of what you are doing! They are to be used only for launching specific tests that require them (__send_analysis_request)

                          • With the following constants in __init__.py, you can customize your tests:

                          • MOCKING_CONNECTIONS: Mock connections to external API to test functions without a real connection or a valid API Key.

                          • If you prefer to use custom inputs for tests, you can change the following constants:

                          • TEST_JOB_ID

                          • TEST_HASH
                          • TEST_URL
                          • TEST_IP
                          • TEST_DOMAIN
                          • TEST_GENERIC
                          • TEST_FILE
                          • TEST_FILE_HASH
                          "},{"location":"pyintelowl/Tests/#launch-tests","title":"Launch Tests","text":"
                          • The test requirements are specified in the test-requirements.txt file. Install them using,

                          .. code-block:: bash

                          $ pip3 install -r test-requirements.txt\n
                          • Launch the tests using tox:

                          .. code-block:: bash

                          $ tox\n
                          "}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..0f8724e --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000..23152d7 Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/static/intel_owl_negative.png b/static/intel_owl_negative.png new file mode 100644 index 0000000..ab19b2b Binary files /dev/null and b/static/intel_owl_negative.png differ diff --git a/static/intel_owl_positive.png b/static/intel_owl_positive.png new file mode 100644 index 0000000..58fba9d Binary files /dev/null and b/static/intel_owl_positive.png differ