diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..b0e9812 --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +max-line-length = 120 +exclude = */payloads_for_tests.py +per-file-ignores = + code/api/__init__.py:E402,F401 + code/api/mapping/__init__.py:E402,F401 + code/api/transformations/__init__.py:E402,F401 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..e950e8d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + pull-request-branch-name: + separator: "-" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..96bdc26 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: Pipenv Install and Test + +on: + push: + branches-ignore: + - main + +jobs: + unittests: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Python, pipenv and Pipfile packages + uses: palewire/install-python-pipenv-pipfile@v4 + with: + python-version: 3.11 + + - name: Run tests + run: | + pipenv install --dev + cd code/ + pipenv run coverage run --source api/ -m pytest --verbose tests/unit/ + pipenv run coverage report --fail-under=80 + + - name: Run pip-audit + run: pipenv run pip-audit diff --git a/Dockerfile b/Dockerfile index c946e9c..9d4b879 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -FROM alpine:3.14 -LABEL maintainer="Ian Redden " +FROM alpine:3.18 +LABEL maintainer="Jyoti Verma jyoverma@cisco.com" -ENV PIP_IGNORE_INSTALLED 1 +ENV PIP_IGNORE_INSTALLED=1 # install packages we need RUN apk update && apk add --no-cache musl-dev openssl-dev gcc py3-configobj \ @@ -10,7 +10,7 @@ py3-pip python3-dev # do the Python dependencies ADD code /app -ADD code/Pipfile code/Pipfile.lock / +ADD Pipfile Pipfile.lock / RUN set -ex && pip install --no-cache-dir --upgrade pipenv && \ pipenv install --system RUN chown -R uwsgi.uwsgi /etc/uwsgi diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fb212a9 --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +NAME_SHORT:="tr-05-rsa-netwitness" +NAME:=ciscosecurity/$(NAME_SHORT) +PORT:="9090" +PLATFORM=--platform linux/amd64,linux/arm64 +VERSION:=$(shell jq '.VERSION' code/container_settings.json | tr -d '"') + +all: env stop build test scout + +run: # app locally + cd code; python -m app; cd - + +# Docker +build: stop + docker buildx build $(PLATFORM) -t $(NAME):$(VERSION) -t $(NAME):latest . +start: build + docker run -dp $(PORT):$(PORT) --name $(NAME_SHORT) $(NAME):$(VERSION) +stop: + docker stop $(NAME_SHORT); docker rm $(NAME_SHORT); true +release: build + docker login + docker image push --all-tags $(NAME) + @echo "https://hub.docker.com/repository/docker/$(NAME)/tags" + +# Tools +env: + pip install --no-cache-dir --upgrade pipenv && pipenv install --dev +black: + black code/ -l 120 -t py311 --exclude=payloads_for_tests.py +lint: black + flake8 code/ + +# Tests +check: lint + curl -sSfL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh | sh -s -- + docker scout cves $(NAME) --only-fixed + pip-audit +test: lint + cd code; coverage run --source api/ -m pytest --verbose tests/unit/ && coverage report --fail-under=80; cd - +test_lf: lint + cd code; coverage run --source api/ -m pytest --verbose -vv --lf tests/unit/ && coverage report -m --fail-under=80; cd - + diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..3f45889 --- /dev/null +++ b/Pipfile @@ -0,0 +1,21 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +cryptography = "==44.0.2" +Flask = "==3.1.0" +marshmallow = "==3.25.1" +requests = "==2.32.3" +PyJWT = "==2.6.0" + +[dev-packages] +black = "==25.1.0" +coverage = "==7.6.12" +flake8 = "==7.1.2" +pip-audit = "==2.8.0" +pytest = "==8.3.5" + +[requires] +python_version = "3.11" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..7323fd0 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,970 @@ +{ + "_meta": { + "hash": { + "sha256": "ee567be64716b34a1edda95fb1db32d4f318ccb58bb1ac8ad9ce3710fa3a253c" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.11" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "blinker": { + "hashes": [ + "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", + "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc" + ], + "markers": "python_version >= '3.9'", + "version": "==1.9.0" + }, + "certifi": { + "hashes": [ + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" + ], + "markers": "python_version >= '3.6'", + "version": "==2025.1.31" + }, + "cffi": { + "hashes": [ + "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", + "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", + "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", + "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", + "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", + "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", + "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", + "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", + "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", + "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", + "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", + "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", + "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", + "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", + "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", + "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", + "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", + "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", + "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", + "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", + "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", + "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", + "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", + "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", + "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", + "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", + "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", + "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", + "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", + "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", + "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", + "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", + "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", + "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", + "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", + "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", + "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", + "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", + "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", + "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", + "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", + "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", + "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", + "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", + "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", + "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", + "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", + "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", + "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", + "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", + "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", + "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", + "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", + "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", + "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", + "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", + "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", + "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", + "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", + "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", + "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", + "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" + ], + "markers": "python_version >= '3.8'", + "version": "==1.17.1" + }, + "charset-normalizer": { + "hashes": [ + "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", + "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", + "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", + "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", + "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", + "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", + "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", + "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", + "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", + "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", + "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", + "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", + "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", + "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", + "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", + "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", + "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", + "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", + "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", + "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", + "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", + "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", + "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", + "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", + "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", + "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", + "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", + "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", + "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", + "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", + "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", + "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", + "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", + "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", + "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", + "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", + "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", + "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", + "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", + "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", + "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", + "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", + "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", + "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", + "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", + "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", + "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", + "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", + "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", + "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", + "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", + "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", + "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", + "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", + "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", + "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", + "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", + "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", + "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", + "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", + "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", + "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", + "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", + "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", + "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", + "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", + "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", + "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", + "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", + "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", + "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", + "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", + "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", + "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", + "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", + "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", + "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", + "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", + "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", + "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", + "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", + "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", + "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", + "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", + "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", + "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", + "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", + "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", + "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", + "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", + "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", + "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.1" + }, + "click": { + "hashes": [ + "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.8" + }, + "cryptography": { + "hashes": [ + "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390", + "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41", + "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688", + "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5", + "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1", + "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d", + "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", + "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843", + "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5", + "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c", + "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a", + "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79", + "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6", + "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181", + "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4", + "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5", + "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562", + "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639", + "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922", + "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3", + "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d", + "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471", + "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd", + "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa", + "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb", + "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699", + "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb", + "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa", + "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0", + "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23", + "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9", + "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615", + "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea", + "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7", + "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308" + ], + "index": "pypi", + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==44.0.2" + }, + "flask": { + "hashes": [ + "sha256:5f873c5184c897c8d9d1b05df1e3d01b14910ce69607a117bd3277098a5836ac", + "sha256:d667207822eb83f1c4b50949b1623c8fc8d51f2341d65f72e1a1815397551136" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==3.1.0" + }, + "idna": { + "hashes": [ + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" + ], + "markers": "python_version >= '3.6'", + "version": "==3.10" + }, + "itsdangerous": { + "hashes": [ + "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", + "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" + ], + "markers": "python_version >= '3.8'", + "version": "==2.2.0" + }, + "jinja2": { + "hashes": [ + "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", + "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.6" + }, + "markupsafe": { + "hashes": [ + "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", + "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", + "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", + "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", + "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", + "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", + "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", + "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", + "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", + "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", + "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", + "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", + "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", + "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", + "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", + "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", + "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", + "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", + "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", + "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", + "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", + "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", + "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", + "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", + "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", + "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", + "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", + "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", + "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", + "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", + "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", + "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", + "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", + "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", + "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", + "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", + "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", + "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", + "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", + "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", + "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", + "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", + "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", + "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", + "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", + "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", + "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", + "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", + "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", + "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", + "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", + "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", + "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", + "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", + "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", + "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", + "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", + "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", + "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", + "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", + "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" + ], + "markers": "python_version >= '3.9'", + "version": "==3.0.2" + }, + "marshmallow": { + "hashes": [ + "sha256:ec5d00d873ce473b7f2ffcb7104286a376c354cab0c2fa12f5573dab03e87210", + "sha256:f4debda3bb11153d81ac34b0d582bf23053055ee11e791b54b4b35493468040a" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==3.25.1" + }, + "packaging": { + "hashes": [ + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" + ], + "markers": "python_version >= '3.8'", + "version": "==24.2" + }, + "pycparser": { + "hashes": [ + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" + ], + "markers": "python_version >= '3.8'", + "version": "==2.22" + }, + "pyjwt": { + "hashes": [ + "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd", + "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==2.6.0" + }, + "requests": { + "hashes": [ + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.32.3" + }, + "urllib3": { + "hashes": [ + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.3.0" + }, + "werkzeug": { + "hashes": [ + "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", + "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746" + ], + "markers": "python_version >= '3.9'", + "version": "==3.1.3" + } + }, + "develop": { + "black": { + "hashes": [ + "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", + "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", + "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", + "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", + "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", + "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", + "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", + "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", + "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", + "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", + "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", + "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", + "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0", + "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", + "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", + "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", + "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355", + "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", + "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e", + "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", + "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", + "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==25.1.0" + }, + "boolean.py": { + "hashes": [ + "sha256:17b9a181630e43dde1851d42bef546d616d5d9b4480357514597e78b203d06e4", + "sha256:2876f2051d7d6394a531d82dc6eb407faa0b01a0a0b3083817ccd7323b8d96bd" + ], + "version": "==4.0" + }, + "cachecontrol": { + "extras": [ + "filecache" + ], + "hashes": [ + "sha256:7d47d19f866409b98ff6025b6a0fca8e4c791fb31abbd95f622093894ce903a2", + "sha256:ebad2091bf12d0d200dfc2464330db638c5deb41d546f6d7aca079e87290f3b0" + ], + "markers": "python_version >= '3.8'", + "version": "==0.14.2" + }, + "certifi": { + "hashes": [ + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" + ], + "markers": "python_version >= '3.6'", + "version": "==2025.1.31" + }, + "charset-normalizer": { + "hashes": [ + "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", + "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", + "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", + "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", + "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", + "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", + "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", + "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", + "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", + "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", + "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", + "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", + "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", + "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", + "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", + "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", + "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", + "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", + "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", + "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", + "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", + "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", + "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", + "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", + "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", + "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", + "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", + "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", + "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", + "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", + "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", + "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", + "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", + "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", + "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", + "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", + "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", + "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", + "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", + "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", + "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", + "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", + "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", + "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", + "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", + "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", + "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", + "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", + "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", + "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", + "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", + "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", + "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", + "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", + "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", + "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", + "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", + "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", + "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", + "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", + "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", + "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", + "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", + "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", + "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", + "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", + "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", + "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", + "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", + "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", + "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", + "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", + "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", + "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", + "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", + "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", + "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", + "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", + "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", + "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", + "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", + "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", + "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", + "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", + "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", + "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", + "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", + "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", + "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", + "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", + "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", + "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.1" + }, + "click": { + "hashes": [ + "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", + "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.8" + }, + "coverage": { + "hashes": [ + "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", + "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", + "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe", + "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0", + "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", + "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", + "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702", + "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", + "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", + "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2", + "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", + "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f", + "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", + "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674", + "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", + "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", + "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", + "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", + "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb", + "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87", + "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", + "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", + "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", + "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", + "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", + "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", + "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", + "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45", + "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa", + "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31", + "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8", + "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86", + "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", + "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", + "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf", + "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929", + "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", + "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", + "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", + "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd", + "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e", + "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879", + "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57", + "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", + "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", + "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba", + "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", + "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", + "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c", + "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c", + "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f", + "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015", + "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558", + "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f", + "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d", + "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d", + "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425", + "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", + "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", + "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", + "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c", + "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", + "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==7.6.12" + }, + "cyclonedx-python-lib": { + "hashes": [ + "sha256:017b95b334aa83b2d0db8af9764e13a46f0e903bd30a57d93d08dcd302c84032", + "sha256:112c6e6e5290420e32026c49b8391645bf3e646c7602f7bdb5d02c6febbaa073" + ], + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==8.9.0" + }, + "defusedxml": { + "hashes": [ + "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", + "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.7.1" + }, + "filelock": { + "hashes": [ + "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", + "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e" + ], + "markers": "python_version >= '3.9'", + "version": "==3.17.0" + }, + "flake8": { + "hashes": [ + "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a", + "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd" + ], + "index": "pypi", + "markers": "python_full_version >= '3.8.1'", + "version": "==7.1.2" + }, + "idna": { + "hashes": [ + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" + ], + "markers": "python_version >= '3.6'", + "version": "==3.10" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "license-expression": { + "hashes": [ + "sha256:679646bc3261a17690494a3e1cada446e5ee342dbd87dcfa4a0c24cc5dce13ee", + "sha256:9f02105f9e0fcecba6a85dfbbed7d94ea1c3a70cf23ddbfb5adf3438a6f6fce0" + ], + "markers": "python_version >= '3.9'", + "version": "==30.4.1" + }, + "markdown-it-py": { + "hashes": [ + "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", + "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" + ], + "markers": "python_version >= '3.8'", + "version": "==3.0.0" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mdurl": { + "hashes": [ + "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", + "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" + ], + "markers": "python_version >= '3.7'", + "version": "==0.1.2" + }, + "msgpack": { + "hashes": [ + "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", + "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", + "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", + "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", + "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", + "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f", + "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", + "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", + "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b", + "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", + "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", + "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", + "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", + "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", + "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", + "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", + "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468", + "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7", + "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", + "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", + "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325", + "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1", + "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846", + "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", + "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", + "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", + "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", + "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", + "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb", + "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", + "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", + "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", + "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", + "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", + "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", + "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", + "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", + "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", + "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", + "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48", + "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb", + "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74", + "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b", + "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346", + "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", + "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", + "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", + "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", + "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", + "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", + "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", + "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", + "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec", + "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8", + "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", + "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", + "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", + "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", + "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870", + "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", + "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96", + "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c", + "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd", + "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788" + ], + "markers": "python_version >= '3.8'", + "version": "==1.1.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "packageurl-python": { + "hashes": [ + "sha256:5c3872638b177b0f1cf01c3673017b7b27ebee485693ae12a8bed70fa7fa7c35", + "sha256:69e3bf8a3932fe9c2400f56aaeb9f86911ecee2f9398dbe1b58ec34340be365d" + ], + "markers": "python_version >= '3.8'", + "version": "==0.16.0" + }, + "packaging": { + "hashes": [ + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" + ], + "markers": "python_version >= '3.8'", + "version": "==24.2" + }, + "pathspec": { + "hashes": [ + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, + "pip": { + "hashes": [ + "sha256:88f96547ea48b940a3a385494e181e29fb8637898f88d88737c5049780f196ea", + "sha256:c46efd13b6aa8279f33f2864459c8ce587ea6a1a59ee20de055868d8f7688f7f" + ], + "markers": "python_version >= '3.8'", + "version": "==25.0.1" + }, + "pip-api": { + "hashes": [ + "sha256:8b2d7d7c37f2447373aa2cf8b1f60a2f2b27a84e1e9e0294a3f6ef10eb3ba6bb", + "sha256:9b75e958f14c5a2614bae415f2adf7eeb54d50a2cfbe7e24fd4826471bac3625" + ], + "markers": "python_version >= '3.8'", + "version": "==0.0.34" + }, + "pip-audit": { + "hashes": [ + "sha256:200f50d56cb6fba3a9189c20d53250354f72f161d63b6ef77ae5de2b53044566", + "sha256:9816cbd94de6f618a8965c117433006b3d565a708dc05d5a7be47ab65b66fa05" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==2.8.0" + }, + "pip-requirements-parser": { + "hashes": [ + "sha256:4659bc2a667783e7a15d190f6fccf8b2486685b6dba4c19c3876314769c57526", + "sha256:b4fa3a7a0be38243123cf9d1f3518da10c51bdb165a2b2985566247f9155a7d3" + ], + "markers": "python_full_version >= '3.6.0'", + "version": "==32.0.1" + }, + "platformdirs": { + "hashes": [ + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" + ], + "markers": "python_version >= '3.8'", + "version": "==4.3.6" + }, + "pluggy": { + "hashes": [ + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.0" + }, + "py-serializable": { + "hashes": [ + "sha256:801be61b0a1ba64c3861f7c624f1de5cfbbabf8b458acc9cdda91e8f7e5effa1", + "sha256:89af30bc319047d4aa0d8708af412f6ce73835e18bacf1a080028bb9e2f42bdb" + ], + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==1.1.2" + }, + "pycodestyle": { + "hashes": [ + "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", + "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521" + ], + "markers": "python_version >= '3.8'", + "version": "==2.12.1" + }, + "pyflakes": { + "hashes": [ + "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", + "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" + ], + "markers": "python_version >= '3.8'", + "version": "==3.2.0" + }, + "pygments": { + "hashes": [ + "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" + ], + "markers": "python_version >= '3.8'", + "version": "==2.19.1" + }, + "pyparsing": { + "hashes": [ + "sha256:506ff4f4386c4cec0590ec19e6302d3aedb992fdc02c761e90416f158dacf8e1", + "sha256:61980854fd66de3a90028d679a954d5f2623e83144b5afe5ee86f43d762e5f0a" + ], + "markers": "python_version >= '3.9'", + "version": "==3.2.1" + }, + "pytest": { + "hashes": [ + "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", + "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==8.3.5" + }, + "requests": { + "hashes": [ + "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.32.3" + }, + "rich": { + "hashes": [ + "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", + "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==13.9.4" + }, + "sortedcontainers": { + "hashes": [ + "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88", + "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0" + ], + "version": "==2.4.0" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==0.10.2" + }, + "urllib3": { + "hashes": [ + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.3.0" + } + } +} diff --git a/code/Pipfile b/code/Pipfile deleted file mode 100644 index 906db61..0000000 --- a/code/Pipfile +++ /dev/null @@ -1,19 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -cryptography = "==3.3.2" -Flask = "==2.0.1" -marshmallow = "==3.12.1" -requests = "==2.25.1" -PyJWT = "==2.1.0" - -[dev-packages] -flake8 = "==3.9.2" -coverage = "==5.5" -pytest = "==6.2.4" - -[requires] -python_version = "3.9" diff --git a/code/Pipfile.lock b/code/Pipfile.lock deleted file mode 100644 index 9bcbd63..0000000 --- a/code/Pipfile.lock +++ /dev/null @@ -1,420 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "fdd746951392809b96e62d08cd666bb3a10042655dedf07c39610f4c56f43326" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.9" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "certifi": { - "hashes": [ - "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", - "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" - ], - "markers": "python_version >= '3.6'", - "version": "==2022.9.24" - }, - "cffi": { - "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" - ], - "version": "==1.15.1" - }, - "chardet": { - "hashes": [ - "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", - "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.0.0" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cryptography": { - "hashes": [ - "sha256:0d7b69674b738068fa6ffade5c962ecd14969690585aaca0a1b1fc9058938a72", - "sha256:1bd0ccb0a1ed775cd7e2144fe46df9dc03eefd722bbcf587b3e0616ea4a81eff", - "sha256:3c284fc1e504e88e51c428db9c9274f2da9f73fdf5d7e13a36b8ecb039af6e6c", - "sha256:49570438e60f19243e7e0d504527dd5fe9b4b967b5a1ff21cc12b57602dd85d3", - "sha256:541dd758ad49b45920dda3b5b48c968f8b2533d8981bcdb43002798d8f7a89ed", - "sha256:5a60d3780149e13b7a6ff7ad6526b38846354d11a15e21068e57073e29e19bed", - "sha256:7951a966613c4211b6612b0352f5bf29989955ee592c4a885d8c7d0f830d0433", - "sha256:922f9602d67c15ade470c11d616f2b2364950602e370c76f0c94c94ae672742e", - "sha256:a0f0b96c572fc9f25c3f4ddbf4688b9b38c69836713fb255f4a2715d93cbaf44", - "sha256:a777c096a49d80f9d2979695b835b0f9c9edab73b59e4ceb51f19724dda887ed", - "sha256:a9a4ac9648d39ce71c2f63fe7dc6db144b9fa567ddfc48b9fde1b54483d26042", - "sha256:aa4969f24d536ae2268c902b2c3d62ab464b5a66bcb247630d208a79a8098e9b", - "sha256:c7390f9b2119b2b43160abb34f63277a638504ef8df99f11cb52c1fda66a2e6f", - "sha256:e18e6ab84dfb0ab997faf8cca25a86ff15dfea4027b986322026cc99e0a892da" - ], - "index": "pypi", - "version": "==3.3.2" - }, - "flask": { - "hashes": [ - "sha256:1c4c257b1892aec1398784c63791cbaa43062f1f7aeb555c4da961b20ee68f55", - "sha256:a6209ca15eb63fc9385f38e452704113d679511d9574d09b2cf9183ae7d20dc9" - ], - "index": "pypi", - "version": "==2.0.1" - }, - "idna": { - "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.10" - }, - "itsdangerous": { - "hashes": [ - "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", - "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.2" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "markupsafe": { - "hashes": [ - "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", - "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", - "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", - "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", - "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", - "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", - "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", - "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", - "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", - "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", - "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", - "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", - "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", - "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", - "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", - "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", - "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", - "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", - "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", - "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", - "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", - "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", - "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", - "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", - "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", - "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", - "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", - "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", - "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", - "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", - "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", - "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", - "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", - "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", - "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", - "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", - "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", - "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", - "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", - "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.1" - }, - "marshmallow": { - "hashes": [ - "sha256:8050475b70470cc58f4441ee92375db611792ba39ca1ad41d39cad193ea9e040", - "sha256:b45cde981d1835145257b4a3c5cb7b80786dcf5f50dd2990749a50c16cb48e01" - ], - "index": "pypi", - "version": "==3.12.1" - }, - "pycparser": { - "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" - ], - "version": "==2.21" - }, - "pyjwt": { - "hashes": [ - "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1", - "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130" - ], - "index": "pypi", - "version": "==2.1.0" - }, - "requests": { - "hashes": [ - "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", - "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" - ], - "index": "pypi", - "version": "==2.25.1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "urllib3": { - "hashes": [ - "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", - "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.12" - }, - "werkzeug": { - "hashes": [ - "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", - "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" - ], - "markers": "python_version >= '3.7'", - "version": "==2.2.2" - } - }, - "develop": { - "attrs": { - "hashes": [ - "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", - "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" - ], - "markers": "python_version >= '3.5'", - "version": "==22.1.0" - }, - "coverage": { - "hashes": [ - "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", - "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6", - "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45", - "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a", - "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03", - "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529", - "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a", - "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a", - "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2", - "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6", - "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759", - "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53", - "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a", - "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4", - "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff", - "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502", - "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793", - "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb", - "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905", - "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821", - "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b", - "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81", - "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0", - "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b", - "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3", - "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184", - "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701", - "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a", - "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82", - "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638", - "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5", - "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083", - "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6", - "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90", - "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465", - "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a", - "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3", - "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e", - "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066", - "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf", - "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b", - "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae", - "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669", - "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873", - "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b", - "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6", - "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb", - "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160", - "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c", - "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079", - "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d", - "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6" - ], - "index": "pypi", - "version": "==5.5" - }, - "flake8": { - "hashes": [ - "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b", - "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907" - ], - "index": "pypi", - "version": "==3.9.2" - }, - "iniconfig": { - "hashes": [ - "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", - "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" - ], - "version": "==1.1.1" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pluggy": { - "hashes": [ - "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", - "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.13.1" - }, - "py": { - "hashes": [ - "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", - "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.11.0" - }, - "pycodestyle": { - "hashes": [ - "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", - "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.7.0" - }, - "pyflakes": { - "hashes": [ - "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", - "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.3.1" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, - "pytest": { - "hashes": [ - "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b", - "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890" - ], - "index": "pypi", - "version": "==6.2.4" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - } - } -} diff --git a/code/api/enrich.py b/code/api/enrich.py index 0c70e3c..d761285 100644 --- a/code/api/enrich.py +++ b/code/api/enrich.py @@ -4,22 +4,21 @@ from api.schemas import ObservableSchema from api.mapping import Mapping -from api.utils import (get_json, query_sightings, - jsonify_result, get_credentials) +from api.utils import get_json, query_sightings, jsonify_result, get_credentials -enrich_api = Blueprint('enrich', __name__) +enrich_api = Blueprint("enrich", __name__) get_observables = partial(get_json, schema=ObservableSchema(many=True)) -@enrich_api.route('/observe/observables', methods=['POST']) +@enrich_api.route("/observe/observables", methods=["POST"]) def observe_observables(): credentials = get_credentials() observables = get_observables() g.sightings = [] for observable in observables: - response = query_sightings(observable['value'], credentials) + response = query_sightings(observable["value"], credentials) for event in response: mapping = Mapping() g.sightings.append(mapping.sighting(observable, event)) diff --git a/code/api/errors.py b/code/api/errors.py index 46c399d..156a00e 100644 --- a/code/api/errors.py +++ b/code/api/errors.py @@ -1,63 +1,43 @@ -AUTH_ERROR = 'authorization error' -INVALID_ARGUMENT = 'invalid argument' -UNKNOWN = 'unknown' -CONNECTION_ERROR = 'connection error' +AUTH_ERROR = "authorization error" +INVALID_ARGUMENT = "invalid argument" +UNKNOWN = "unknown" +CONNECTION_ERROR = "connection error" class TRFormattedError(Exception): - def __init__(self, code, message, type_='fatal'): + def __init__(self, code, message, type_="fatal"): super().__init__() self.code = code or UNKNOWN - self.message = message or 'Something went wrong.' + self.message = message or "Something went wrong." self.type_ = type_ @property def json(self): - return {'type': self.type_, - 'code': self.code, - 'message': self.message} + return {"type": self.type_, "code": self.code, "message": self.message} class AuthorizationError(TRFormattedError): def __init__(self, message): - super().__init__( - AUTH_ERROR, - f'Authorization failed: {message}' - ) + super().__init__(AUTH_ERROR, f"Authorization failed: {message}") class WatchdogError(TRFormattedError): def __init__(self): - super().__init__( - code='health check failed', - message='Invalid Health Check' - ) + super().__init__(code="health check failed", message="Invalid Health Check") class InvalidArgumentError(TRFormattedError): def __init__(self, message): - super().__init__( - INVALID_ARGUMENT, - str(message) - ) + super().__init__(INVALID_ARGUMENT, str(message)) class RSANetwitnessSSLError(TRFormattedError): def __init__(self, error): - message = getattr( - error.args[0].reason.args[0], 'verify_message', '' - ) or error.args[0].reason.args[0].args[0] + message = getattr(error.args[0].reason.args[0], "verify_message", "") or error.args[0].reason.args[0].args[0] - super().__init__( - UNKNOWN, - f'Unable to verify SSL certificate: {message}' - ) + super().__init__(UNKNOWN, f"Unable to verify SSL certificate: {message}") class RSANetwitnessConnectionError(TRFormattedError): def __init__(self): - super().__init__( - CONNECTION_ERROR, - 'Unable to connect to RSA Netwitness,' - ' validate the configured API endpoint' - ) + super().__init__(CONNECTION_ERROR, "Unable to connect to RSA Netwitness, validate the configured API endpoint") diff --git a/code/api/health.py b/code/api/health.py index a0ee870..d3252a7 100644 --- a/code/api/health.py +++ b/code/api/health.py @@ -2,12 +2,12 @@ from api.utils import jsonify_data, get_node_info, get_credentials -health_api = Blueprint('health', __name__) +health_api = Blueprint("health", __name__) -@health_api.route('/health', methods=['POST']) +@health_api.route("/health", methods=["POST"]) def health(): credentials = get_credentials() _ = get_node_info(credentials) - return jsonify_data({'status': 'ok'}) + return jsonify_data({"status": "ok"}) diff --git a/code/api/mapping.py b/code/api/mapping.py index 80a9c5f..444d7d1 100644 --- a/code/api/mapping.py +++ b/code/api/mapping.py @@ -1,35 +1,26 @@ from uuid import uuid4 + from flask import current_app, g class Mapping: @staticmethod - def formatTime(datestamp): + def format_time(datestamp): return datestamp # return datestamp.isoformat(timespec="milliseconds") def observed_time(self, event): - event_time = self.formatTime(event['time']) - return { - 'start_time': event_time, - 'end_time': event_time - } + event_time = self.format_time(event["time"]) + return {"start_time": event_time, "end_time": event_time} @staticmethod def get_relations(event): - if ((event.get("ip_src") and event.get("ip_dst")) and - (event["ip_src"] != event["ip_dst"])): + if (event.get("ip_src") and event.get("ip_dst")) and (event["ip_src"] != event["ip_dst"]): return [ { - "related": { - "type": "ip", - "value": event['ip_dst'] - }, - "source": { - "type": "ip", - "value": event["ip_src"] - }, - **current_app.config['RELATIONS_DEFAULTS'] + "related": {"type": "ip", "value": event["ip_dst"]}, + "source": {"type": "ip", "value": event["ip_src"]}, + **current_app.config["RELATIONS_DEFAULTS"], } ] else: @@ -41,36 +32,31 @@ def observables(self, event): def targets(self, event): observables = [] - if event.get('ip_dst'): - observables.append({'type': 'ip', - 'value': event['ip_dst']}) - if event.get('filename'): - observables.append({'type': 'file_name', - 'value': event['filename']}) - if event.get('eth_dst'): - observables.append({'type': 'mac_address', - 'value': event['eth_dst']}) - if event.get('username'): - observables.append({'type': 'user', - 'value': event['username']}) - if event.get('hostname'): - observables.append({'type': 'hostname', - 'value': event['hostname']}) + if event.get("ip_dst"): + observables.append({"type": "ip", "value": event["ip_dst"]}) + if event.get("filename"): + observables.append({"type": "file_name", "value": event["filename"]}) + if event.get("eth_dst"): + observables.append({"type": "mac_address", "value": event["eth_dst"]}) + if event.get("username"): + observables.append({"type": "user", "value": event["username"]}) + if event.get("hostname"): + observables.append({"type": "hostname", "value": event["hostname"]}) if not observables: return [] target = { - 'observables': observables, - 'observed_time': self.observed_time(event), - 'type': 'endpoint', + "observables": observables, + "observed_time": self.observed_time(event), + "type": "endpoint", } return [target] def sighting(self, observable, event): - if event.get('packets'): - count = event['packets'] + if event.get("packets"): + count = event["packets"] else: count = 1 @@ -78,28 +64,24 @@ def sighting(self, observable, event): mapping_str = [] for item in event: if item in link_keys: - mapping_str.append( - "{}: [{}](/investigate?q={})\n".format(item, event[item], event[item])) + mapping_str.append("{}: [{}](/investigate?q={})\n".format(item, event[item], event[item])) else: mapping_str.append("{}: {}\n".format(item, event[item])) metadata = "- ".join(mapping_str) + print(f"{event=}") d = { - 'id': f'sighting-{uuid4()}', - 'targets': self.targets(event), - 'relations': self.get_relations(event), - 'count': int(count), - 'observed_time': self.observed_time(event), - 'observables': self.observables(event), - 'short_description': - f"RSA Netwitness session ID {event['sessionid']} ({event['netname']})", - 'description': 'RSA Netwitness session ID ' - f'{event["sessionid"]} retrieved from decoder ' - f'{event["did"]} related to ' - f'{observable["value"]}' - f'\n\nNetWitness Mapped Values:\n- {metadata}\n\n', - **current_app.config['SIGHTING_DEFAULTS'] + "id": f"transient:sighting-{uuid4()}", + "targets": self.targets(event), + "relations": self.get_relations(event), + "count": int(count), + "observed_time": self.observed_time(event), + "observables": self.observables(event), + "short_description": f"RSA Netwitness session ID {event['sessionid']} ({event.get('netname')})", + "description": f"RSA Netwitness session ID {event['sessionid']} retrieved from decoder " + f"{event['did']} related to {observable['value']}\n\nNetWitness Mapped Values:\n- {metadata}\n\n", + **current_app.config["SIGHTING_DEFAULTS"], } return d diff --git a/code/api/schemas.py b/code/api/schemas.py index b82a6e5..73d563c 100644 --- a/code/api/schemas.py +++ b/code/api/schemas.py @@ -2,8 +2,8 @@ def validate_string(value): - if value == '': - raise ValidationError('Field may not be blank.') + if value == "": + raise ValidationError("Field may not be blank.") class ObservableSchema(Schema): @@ -20,11 +20,11 @@ class ObservableSchema(Schema): class NetwitnessSchema(Schema): sessionid = fields.Str(required=True) time = fields.DateTime(required=True) - eth_src = fields.Str(required=False, data_key='eth.src') - eth_dst = fields.Str(required=False, data_key='eth.dst') - ip_src = fields.Str(required=False, data_key='ip.src') - ip_dst = fields.Str(required=False, data_key='ip.dst') - proto = fields.Str(required=False, data_key='ip.proto') + eth_src = fields.Str(required=False, data_key="eth.src") + eth_dst = fields.Str(required=False, data_key="eth.dst") + ip_src = fields.Str(required=False, data_key="ip.src") + ip_dst = fields.Str(required=False, data_key="ip.dst") + proto = fields.Str(required=False, data_key="ip.proto") service = fields.Str(required=False) netname = fields.Str(required=False) direction = fields.Str(required=False) @@ -32,4 +32,4 @@ class NetwitnessSchema(Schema): username = fields.Str(required=False) packets = fields.Str(required=False) did = fields.Str(required=False) - domain = fields.Str(required=False, data_key='alias.host') + domain = fields.Str(required=False, data_key="alias.host") diff --git a/code/api/utils.py b/code/api/utils.py index a96c44d..a3cbda3 100644 --- a/code/api/utils.py +++ b/code/api/utils.py @@ -6,25 +6,22 @@ import requests from flask import request, current_app, jsonify, g from jwt import InvalidSignatureError, DecodeError, InvalidAudienceError -from requests.exceptions import (InvalidURL, HTTPError, - SSLError, ConnectionError) - -from api.errors import (AuthorizationError, InvalidArgumentError, - RSANetwitnessSSLError, RSANetwitnessConnectionError) - -NO_AUTH_HEADER = 'Authorization header is missing' -WRONG_AUTH_TYPE = 'Wrong authorization type' -WRONG_PAYLOAD_STRUCTURE = 'Wrong JWT payload structure' -WRONG_JWT_STRUCTURE = 'Wrong JWT structure' -WRONG_AUDIENCE = 'Wrong configuration-token-audience' -KID_NOT_FOUND = 'kid from JWT header not found in API response' -WRONG_KEY = ('Failed to decode JWT with provided key. ' - 'Make sure domain in custom_jwks_host ' - 'corresponds to your SecureX instance region.') -JWKS_HOST_MISSING = ('jwks_host is missing in JWT payload. Make sure ' - 'custom_jwks_host field is present in module_type') -WRONG_JWKS_HOST = ('Wrong jwks_host in JWT payload. Make sure domain follows ' - 'the visibility..cisco.com structure') +from requests.exceptions import InvalidURL, HTTPError, SSLError, ConnectionError + +from api.errors import AuthorizationError, InvalidArgumentError, RSANetwitnessSSLError, RSANetwitnessConnectionError + +NO_AUTH_HEADER = "Authorization header is missing" +WRONG_AUTH_TYPE = "Wrong authorization type" +WRONG_PAYLOAD_STRUCTURE = "Wrong JWT payload structure" +WRONG_JWT_STRUCTURE = "Wrong JWT structure" +WRONG_AUDIENCE = "Wrong configuration-token-audience" +KID_NOT_FOUND = "kid from JWT header not found in API response" +WRONG_KEY = ( + "Failed to decode JWT with provided key. " + "Make sure domain in custom_jwks_host corresponds to your SecureX instance region." +) +JWKS_HOST_MISSING = "jwks_host is missing in JWT payload. Make sure custom_jwks_host field is present in module_type" +WRONG_JWKS_HOST = "Wrong jwks_host in JWT payload. Make sure domain follows the visibility..cisco.com structure" def get_public_key(jwks_host, token): @@ -35,25 +32,17 @@ def get_public_key(jwks_host, token): any way, replaced by another function, or even removed from the module. """ - expected_errors = ( - ConnectionError, - InvalidURL, - KeyError, - JSONDecodeError, - HTTPError - ) + expected_errors = (ConnectionError, InvalidURL, KeyError, JSONDecodeError, HTTPError) try: response = requests.get(f"https://{jwks_host}/.well-known/jwks") response.raise_for_status() jwks = response.json() public_keys = {} - for jwk in jwks['keys']: - kid = jwk['kid'] - public_keys[kid] = jwt.algorithms.RSAAlgorithm.from_jwk( - json.dumps(jwk) - ) - kid = jwt.get_unverified_header(token)['kid'] + for jwk in jwks["keys"]: + kid = jwk["kid"] + public_keys[kid] = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk)) + kid = jwt.get_unverified_header(token)["kid"] return public_keys.get(kid) except expected_errors: @@ -67,13 +56,10 @@ def get_auth_token(): anything before passing to an API endpoint, and thus it may be modified in any way, replaced by another function, or even removed from the module. """ - expected_errors = { - KeyError: NO_AUTH_HEADER, - AssertionError: WRONG_AUTH_TYPE - } + expected_errors = {KeyError: NO_AUTH_HEADER, AssertionError: WRONG_AUTH_TYPE} try: - scheme, token = request.headers['Authorization'].split() - assert scheme.lower() == 'bearer' + scheme, token = request.headers["Authorization"].split() + assert scheme.lower() == "bearer" return token except tuple(expected_errors) as error: raise AuthorizationError(expected_errors[error.__class__]) @@ -94,21 +80,17 @@ def get_credentials(): InvalidSignatureError: WRONG_KEY, DecodeError: WRONG_JWT_STRUCTURE, InvalidAudienceError: WRONG_AUDIENCE, - TypeError: KID_NOT_FOUND + TypeError: KID_NOT_FOUND, } token = get_auth_token() try: - jwks_host = jwt.decode( - token, options={'verify_signature': False} - )['jwks_host'] + jwks_host = jwt.decode(token, options={"verify_signature": False})["jwks_host"] key = get_public_key(jwks_host, token) aud = request.url_root - payload = jwt.decode( - token, key=key, algorithms=['RS256'], audience=[aud.rstrip('/')] - ) - assert 'username' in payload - assert 'password' in payload - assert 'url' in payload + payload = jwt.decode(token, key=key, algorithms=["RS256"], audience=[aud.rstrip("/")]) + assert "username" in payload + assert "password" in payload + assert "url" in payload return payload except tuple(expected_errors) as error: @@ -135,18 +117,18 @@ def wraps(*args, **kwargs): raise RSANetwitnessSSLError(error) except (ConnectionError, InvalidURL): raise RSANetwitnessConnectionError + return wraps @catch_errors def get_node_info(credentials): - url = credentials.get('url') - username = credentials.get('username') - password = credentials.get('password') + url = credentials.get("url") + username = credentials.get("username") + password = credentials.get("password") # format the query url - NW_URL = f'{url}/sdk?msg=info&' \ - f'force-content-type=application/json' + NW_URL = f"{url}/sdk?msg=info&" f"force-content-type=application/json" r = requests.get(NW_URL, auth=(username, password)) json_result = r.json() @@ -155,22 +137,24 @@ def get_node_info(credentials): @catch_errors def query_sightings(indicator, credentials): - url = credentials.get('url') - username = credentials.get('username') - password = credentials.get('password') - time_window = current_app.config['SEARCH_TIMEFRAME'] + url = credentials.get("url") + username = credentials.get("username") + password = credentials.get("password") + time_window = current_app.config["SEARCH_TIMEFRAME"] # format the query url - NW_URL = f'{url}/sdk?msg=query&' \ - f'force-content-type=application/json&' \ - f'query=select+packets%2C+did%2C+sessionid%2C+time%2C+ip.src' \ - f'%2C+ip.dst%2C+ip.proto%2C+filename%2C+username%2C+service%2' \ - f'C+alias.host%2C+netname%2C+direction%2C+eth.src%2C+eth.dst+' \ - f'WHERE+time+%3D+rtp%28latest%2C+{time_window}h%29+-+u+' \ - f'GROUP+BY+sessionid+ORDER+BY+time+DESC&' \ - f'search={indicator}&' \ - f'id1=0&id2=0&flags=0&' \ - f'size={current_app.config["MAX_SEARCH_LIMIT"]}' + NW_URL = ( + f"{url}/sdk?msg=query&" + f"force-content-type=application/json&" + f"query=select+packets%2C+did%2C+sessionid%2C+time%2C+ip.src" + f"%2C+ip.dst%2C+ip.proto%2C+filename%2C+username%2C+service%2" + f"C+alias.host%2C+netname%2C+direction%2C+eth.src%2C+eth.dst+" + f"WHERE+time+%3D+rtp%28latest%2C+{time_window}h%29+-+u+" + f"GROUP+BY+sessionid+ORDER+BY+time+DESC&" + f"search={indicator}&" + f"id1=0&id2=0&flags=0&" + f'size={current_app.config["MAX_SEARCH_LIMIT"]}' + ) r = requests.get(NW_URL, auth=(username, password)) json_result = r.json() @@ -182,26 +166,26 @@ def query_sightings(indicator, credentials): group_fields = {} session = [] # populate group fields with data - if json_result.get('results'): - for field in json_result['results']['fields']: - group_id = field['group'] + if json_result.get("results"): + for field in json_result["results"]["fields"]: + group_id = field["group"] # initialize if not if group_id not in group_fields: group_fields[group_id] = {} - type_field = field['type'] + type_field = field["type"] - if field['format'] == 32: - value_field = convertEpochTime(field['value']) + if field["format"] == 32: + value_field = convertEpochTime(field["value"]) else: - value_field = field['value'] + value_field = field["value"] group_fields[group_id][type_field] = value_field sessions = [] for k, session in group_fields.items(): - if 'time' in session: + if "time" in session: sessions.append(session) else: sessions = [] @@ -210,24 +194,21 @@ def query_sightings(indicator, credentials): def jsonify_data(data): - return jsonify({'data': data}) + return jsonify({"data": data}) def jsonify_errors(data): - return jsonify({'errors': [data]}) + return jsonify({"errors": [data]}) def getLastTime(url, username, password): - time_result = doNWQuery("select max(time)", url, - username, password) - return datetime.datetime.utcfromtimestamp( - time_result[0]['results']['fields'][0]['value']) + time_result = doNWQuery("select max(time)", url, username, password) + return datetime.datetime.utcfromtimestamp(time_result[0]["results"]["fields"][0]["value"]) @catch_errors def doNWQuery(query, url, username, password, limit=1): - NW_URL = f'{url}/sdk?msg=query&fo' \ - f'rce-content-type=application/json&query={query}&limit={limit}' + NW_URL = f"{url}/sdk?msg=query&fo" f"rce-content-type=application/json&query={query}&limit={limit}" r = requests.get(NW_URL, auth=(username, password)) json_result = r.json() @@ -236,26 +217,25 @@ def doNWQuery(query, url, username, password, limit=1): def convertEpochTime(epoch): - return datetime.datetime.fromtimestamp( - epoch, datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.000000Z') + return datetime.datetime.fromtimestamp(epoch, datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000000Z") def format_docs(docs): - return {'count': len(docs), 'docs': docs} + return {"count": len(docs), "docs": docs} def jsonify_result(): - result = {'data': {}} + result = {"data": {}} - if g.get('status'): - result['data']['status'] = g.status + if g.get("status"): + result["data"]["status"] = g.status - if g.get('sightings'): - result['data']['sightings'] = format_docs(g.sightings) + if g.get("sightings"): + result["data"]["sightings"] = format_docs(g.sightings) - if g.get('errors'): - result['errors'] = g.errors - if not result['data']: - del result['data'] + if g.get("errors"): + result["errors"] = g.errors + if not result["data"]: + del result["data"] return jsonify(result) diff --git a/code/api/version.py b/code/api/version.py index 35bc6db..567915c 100644 --- a/code/api/version.py +++ b/code/api/version.py @@ -1,8 +1,8 @@ from flask import Blueprint, jsonify, current_app -version_api = Blueprint('version', __name__) +version_api = Blueprint("version", __name__) -@version_api.route('/version', methods=['POST']) +@version_api.route("/version", methods=["POST"]) def version(): - return jsonify({'version': current_app.config['VERSION']}) + return jsonify({"version": current_app.config["VERSION"]}) diff --git a/code/api/watchdog.py b/code/api/watchdog.py index 1e517c8..2939058 100644 --- a/code/api/watchdog.py +++ b/code/api/watchdog.py @@ -2,13 +2,13 @@ from flask import request, Blueprint from api.errors import WatchdogError -watchdog_api = Blueprint('watchdog', __name__) +watchdog_api = Blueprint("watchdog", __name__) -@watchdog_api.route('/watchdog', methods=['GET']) +@watchdog_api.route("/watchdog", methods=["GET"]) def watchdog(): try: - watchdog_key = request.headers['Health-Check'] + watchdog_key = request.headers["Health-Check"] return jsonify_data(watchdog_key) except KeyError: raise WatchdogError diff --git a/code/app.py b/code/app.py index 16323eb..213814a 100644 --- a/code/app.py +++ b/code/app.py @@ -12,7 +12,7 @@ app = Flask(__name__) app.url_map.strict_slashes = False -app.config.from_object('config.Config') +app.config.from_object("config.Config") app.register_blueprint(enrich_api) app.register_blueprint(health_api) @@ -22,12 +22,14 @@ @app.errorhandler(Exception) def handle_error(exception): - code = getattr(exception, 'code', 500) - message = getattr(exception, 'description', 'Something went wrong.') - reason = '.'.join([ - exception.__class__.__module__, - exception.__class__.__name__, - ]) + code = getattr(exception, "code", 500) + message = getattr(exception, "description", "Something went wrong.") + reason = ".".join( + [ + exception.__class__.__module__, + exception.__class__.__name__, + ] + ) if code != 404: app.logger.error(traceback.format_exc()) @@ -42,5 +44,5 @@ def handle_tr_formatted_error(exception): return jsonify_errors(exception.json) -if __name__ == '__main__': - app.run() +if __name__ == "__main__": + app.run(host="localhost", port=9090) diff --git a/code/config.py b/code/config.py index 02fe095..2e8132c 100644 --- a/code/config.py +++ b/code/config.py @@ -2,26 +2,23 @@ class Config: - settings = json.load(open('container_settings.json', 'r')) - VERSION = settings['VERSION'] - APIPORT = '50105' + settings = json.load(open("container_settings.json", "r")) + VERSION = settings["VERSION"] + APIPORT = "50105" MAX_SEARCH_LIMIT = 300 - SEARCH_TIMEFRAME = 24 # hours + SEARCH_TIMEFRAME = 24 # hours - SOURCE = 'RSA NetWitness' + SOURCE = "RSA NetWitness" CTIM_DEFAULTS = { - 'schema_version': '1.1.4', + "schema_version": "1.1.4", } SIGHTING_DEFAULTS = { **CTIM_DEFAULTS, - 'confidence': 'High', - 'type': 'sighting', - 'source': SOURCE, + "confidence": "High", + "type": "sighting", + "source": SOURCE, } - RELATIONS_DEFAULTS = { - "origin": SOURCE, - "relation": 'Connected_To' - } + RELATIONS_DEFAULTS = {"origin": SOURCE, "relation": "Connected_To"} diff --git a/code/tests/unit/api/test_authorization.py b/code/tests/unit/api/test_authorization.py index 5e9924a..94cf356 100644 --- a/code/tests/unit/api/test_authorization.py +++ b/code/tests/unit/api/test_authorization.py @@ -6,167 +6,98 @@ from api.errors import AUTH_ERROR from tests.unit.api.utils import get_headers from tests.unit.conftest import mock_api_response -from api.utils import ( - WRONG_PAYLOAD_STRUCTURE, - WRONG_KEY, - WRONG_AUDIENCE, - KID_NOT_FOUND, - JWKS_HOST_MISSING -) -from tests.unit.payloads_for_tests import ( - EXPECTED_RESPONSE_OF_JWKS_ENDPOINT, - RESPONSE_OF_JWKS_ENDPOINT_WITH_WRONG_KEY -) +from api.utils import WRONG_PAYLOAD_STRUCTURE, WRONG_KEY, WRONG_AUDIENCE, KID_NOT_FOUND, JWKS_HOST_MISSING +from tests.unit.payloads_for_tests import EXPECTED_RESPONSE_OF_JWKS_ENDPOINT, RESPONSE_OF_JWKS_ENDPOINT_WITH_WRONG_KEY def routes(): - yield '/observe/observables' + yield "/observe/observables" -@fixture(scope='module', params=routes(), ids=lambda route: f'POST {route}') +@fixture(scope="module", params=routes(), ids=lambda route: f"POST {route}") def route(request): return request.param -@fixture(scope='module') +@fixture(scope="module") def wrong_jwt_structure(): - return 'wrong_jwt_structure' + return "wrong_jwt_structure" -@fixture(scope='module') +@fixture(scope="module") def authorization_errors_expected_payload(route): def _make_payload_message(message): - payload = { - 'errors': [{ - 'code': AUTH_ERROR, - 'message': f'Authorization failed: {message}', - 'type': 'fatal'}] - - } + payload = {"errors": [{"code": AUTH_ERROR, "message": f"Authorization failed: {message}", "type": "fatal"}]} return payload return _make_payload_message -def test_call_with_authorization_header_failure( - route, client, - authorization_errors_expected_payload -): +def test_call_with_authorization_header_failure(route, client, authorization_errors_expected_payload): response = client.post(route) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - 'Authorization header is missing' - ) + assert response.json == authorization_errors_expected_payload("Authorization header is missing") -def test_call_with_wrong_authorization_type( - route, client, valid_jwt, - authorization_errors_expected_payload -): - response = client.post( - route, headers=get_headers(valid_jwt(), auth_type='wrong_type') - ) +def test_call_with_wrong_authorization_type(route, client, valid_jwt, authorization_errors_expected_payload): + response = client.post(route, headers=get_headers(valid_jwt(), auth_type="wrong_type")) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - 'Wrong authorization type' - ) + assert response.json == authorization_errors_expected_payload("Wrong authorization type") -def test_call_with_wrong_jwt_structure( - route, client, wrong_jwt_structure, - authorization_errors_expected_payload -): +def test_call_with_wrong_jwt_structure(route, client, wrong_jwt_structure, authorization_errors_expected_payload): response = client.post(route, headers=get_headers(wrong_jwt_structure)) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - 'Wrong JWT structure' - ) + assert response.json == authorization_errors_expected_payload("Wrong JWT structure") -@patch('requests.get') +@patch("requests.get") def test_call_with_jwt_encoded_by_wrong_key( - mock_request, route, - client, valid_jwt, - authorization_errors_expected_payload + mock_request, route, client, valid_jwt, authorization_errors_expected_payload ): - mock_request.return_value = \ - mock_api_response(payload=RESPONSE_OF_JWKS_ENDPOINT_WITH_WRONG_KEY) + mock_request.return_value = mock_api_response(payload=RESPONSE_OF_JWKS_ENDPOINT_WITH_WRONG_KEY) response = client.post(route, headers=get_headers(valid_jwt())) assert response.status_code == HTTPStatus.OK assert response.json == authorization_errors_expected_payload(WRONG_KEY) -@patch('requests.get') +@patch("requests.get") def test_call_with_wrong_jwt_payload_structure( - mock_request, - route, client, valid_jwt, - authorization_errors_expected_payload + mock_request, route, client, valid_jwt, authorization_errors_expected_payload ): - mock_request.return_value = \ - mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) - response = \ - client.post(route, - headers=get_headers(valid_jwt(wrong_structure=True))) + mock_request.return_value = mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) + response = client.post(route, headers=get_headers(valid_jwt(wrong_structure=True))) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - WRONG_PAYLOAD_STRUCTURE - ) + assert response.json == authorization_errors_expected_payload(WRONG_PAYLOAD_STRUCTURE) -@patch('requests.get') -def test_call_with_wrong_audience( - mock_request, route, client, valid_jwt, - authorization_errors_expected_payload -): - mock_request.return_value = \ - mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) +@patch("requests.get") +def test_call_with_wrong_audience(mock_request, route, client, valid_jwt, authorization_errors_expected_payload): + mock_request.return_value = mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) - response = client.post( - route, - headers=get_headers(valid_jwt(aud='wrong_aud')) - ) + response = client.post(route, headers=get_headers(valid_jwt(aud="wrong_aud"))) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - WRONG_AUDIENCE - ) + assert response.json == authorization_errors_expected_payload(WRONG_AUDIENCE) -@patch('requests.get') -def test_call_with_wrong_kid( - mock_request, route, client, valid_jwt, - authorization_errors_expected_payload -): - mock_request.return_value = \ - mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) +@patch("requests.get") +def test_call_with_wrong_kid(mock_request, route, client, valid_jwt, authorization_errors_expected_payload): + mock_request.return_value = mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) - response = client.post( - route, - headers=get_headers(valid_jwt(kid='wrong_kid')) - ) + response = client.post(route, headers=get_headers(valid_jwt(kid="wrong_kid"))) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - KID_NOT_FOUND - ) + assert response.json == authorization_errors_expected_payload(KID_NOT_FOUND) -@patch('requests.get') -def test_call_with_missing_jwks_host( - mock_request, route, client, valid_jwt, - authorization_errors_expected_payload -): - mock_request.return_value = \ - mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) +@patch("requests.get") +def test_call_with_missing_jwks_host(mock_request, route, client, valid_jwt, authorization_errors_expected_payload): + mock_request.return_value = mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) - response = client.post( - route, - headers=get_headers(valid_jwt(wrong_jwks_host=True)) - ) + response = client.post(route, headers=get_headers(valid_jwt(wrong_jwks_host=True))) assert response.status_code == HTTPStatus.OK - assert response.json == authorization_errors_expected_payload( - JWKS_HOST_MISSING - ) + assert response.json == authorization_errors_expected_payload(JWKS_HOST_MISSING) diff --git a/code/tests/unit/api/test_enrich.py b/code/tests/unit/api/test_enrich.py index 1f3cab1..89681a1 100644 --- a/code/tests/unit/api/test_enrich.py +++ b/code/tests/unit/api/test_enrich.py @@ -5,13 +5,15 @@ from tests.unit.api.utils import get_headers from tests.unit.conftest import mock_api_response -from tests.unit.payloads_for_tests import (EXPECTED_RESPONSE_OF_JWKS_ENDPOINT, - EXPECTED_PAYLOAD_SEARCH, - EXPECTED_RESPONSE_OBSERVE,) +from tests.unit.payloads_for_tests import ( + EXPECTED_RESPONSE_OF_JWKS_ENDPOINT, + EXPECTED_PAYLOAD_SEARCH, + EXPECTED_RESPONSE_OBSERVE, +) def routes(): - yield '/observe/observables' + yield "/observe/observables" def expected_responses(): @@ -20,52 +22,43 @@ def expected_responses(): def ids(): - yield '63b847c9-4429-4328-804a-5a06c2d03f9f' - yield 'c54d2bfa-a710-42af-92f2-f3a628b89c15' - yield '806b4eb2-18da-4322-bf7d-3701823a87e5' - yield 'da992d05-3f6e-4433-89cd-67208df067c8' - yield '78a6ba52-4353-43b2-9308-b5886f387682' + yield "63b847c9-4429-4328-804a-5a06c2d03f9f" + yield "c54d2bfa-a710-42af-92f2-f3a628b89c15" + yield "806b4eb2-18da-4322-bf7d-3701823a87e5" + yield "da992d05-3f6e-4433-89cd-67208df067c8" + yield "78a6ba52-4353-43b2-9308-b5886f387682" -@fixture(scope='module', params=routes(), ids=lambda route: f'POST {route}') +@fixture(scope="module", params=routes(), ids=lambda route: f"POST {route}") def route(request): return request.param -@fixture(scope='module') +@fixture(scope="module") def invalid_json_value(): - return [{'type': 'ip', 'value': ''}] + return [{"type": "ip", "value": ""}] -@patch('requests.get') +@patch("requests.get") def test_enrich_call_with_valid_jwt_but_invalid_json_value( - mock_request, - route, client, valid_jwt, invalid_json_value, - invalid_json_expected_payload + mock_request, route, client, valid_jwt, invalid_json_value, invalid_json_expected_payload ): - mock_request.return_value = \ - mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) - response = client.post(route, - headers=get_headers(valid_jwt()), - json=invalid_json_value) + mock_request.return_value = mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) + response = client.post(route, headers=get_headers(valid_jwt()), json=invalid_json_value) assert response.status_code == HTTPStatus.OK - assert response.json == invalid_json_expected_payload( - "{0: {'value': ['Field may not be blank.']}}" - ) + assert response.json == invalid_json_expected_payload("{0: {'value': ['Field may not be blank.']}}") -@fixture(scope='module') +@fixture(scope="module") def valid_json(): - return [{'type': 'ip', 'value': '52.3.199.104'}] + return [{"type": "ip", "value": "52.3.199.104"}] -@patch('api.mapping.uuid4') -@patch('requests.get') -def test_enrich_call_success(mock_request, mock_id, - route, client, valid_jwt, valid_json): +@patch("api.mapping.uuid4") +@patch("requests.get") +def test_enrich_call_success(mock_request, mock_id, route, client, valid_jwt, valid_json): mock_id.side_effect = ids() mock_request.side_effect = expected_responses() - response = client.post(route, headers=get_headers(valid_jwt()), - json=valid_json) + response = client.post(route, headers=get_headers(valid_jwt()), json=valid_json) assert response.status_code == HTTPStatus.OK assert response.json == EXPECTED_RESPONSE_OBSERVE diff --git a/code/tests/unit/api/test_health.py b/code/tests/unit/api/test_health.py index 4164e7f..4f5815c 100644 --- a/code/tests/unit/api/test_health.py +++ b/code/tests/unit/api/test_health.py @@ -9,20 +9,18 @@ def routes(): - yield '/health' + yield "/health" -@fixture(scope='module', params=routes(), ids=lambda route: f'POST {route}') +@fixture(scope="module", params=routes(), ids=lambda route: f"POST {route}") def route(request): return request.param -@patch('requests.get') -def test_health_call_success(mock_get, - route, client, valid_jwt): - mock_get.return_value = \ - mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) +@patch("requests.get") +def test_health_call_success(mock_get, route, client, valid_jwt): + mock_get.return_value = mock_api_response(payload=EXPECTED_RESPONSE_OF_JWKS_ENDPOINT) # mock_request.return_value = mock_api_response() response = client.post(route, headers=get_headers(valid_jwt())) assert response.status_code == HTTPStatus.OK - assert response.json == {'data': {'status': 'ok'}} + assert response.json == {"data": {"status": "ok"}} diff --git a/code/tests/unit/api/test_version.py b/code/tests/unit/api/test_version.py index e85e680..2d7b432 100644 --- a/code/tests/unit/api/test_version.py +++ b/code/tests/unit/api/test_version.py @@ -4,18 +4,18 @@ def routes(): - yield '/version' + yield "/version" -@fixture(scope='module', params=routes(), ids=lambda route: f'POST {route}') +@fixture(scope="module", params=routes(), ids=lambda route: f"POST {route}") def route(request): return request.param -@fixture(scope='module') +@fixture(scope="module") def version_expected_payload(client): app = client.application - return {'version': app.config['VERSION']} + return {"version": app.config["VERSION"]} def test_version_call_success(route, client, version_expected_payload): diff --git a/code/tests/unit/api/test_watchdog.py b/code/tests/unit/api/test_watchdog.py index e3d00e0..85ebb5f 100644 --- a/code/tests/unit/api/test_watchdog.py +++ b/code/tests/unit/api/test_watchdog.py @@ -3,18 +3,18 @@ def routes(): - yield '/watchdog' + yield "/watchdog" -@fixture(scope='module', params=routes(), ids=lambda route: f'GET {route}') +@fixture(scope="module", params=routes(), ids=lambda route: f"GET {route}") def route(request): return request.param def test_watchdog_call_success(route, client): - response = client.get(route, headers={'Health-Check': 'test'}) + response = client.get(route, headers={"Health-Check": "test"}) - expected_payload = {'data': 'test'} + expected_payload = {"data": "test"} assert response.status_code == HTTPStatus.OK assert response.get_json() == expected_payload @@ -23,15 +23,7 @@ def test_watchdog_call_success(route, client): def test_watchdog_call_with_missing_header(route, client): response = client.get(route) - expected_payload = { - 'errors': [ - { - 'code': 'health check failed', - 'message': 'Invalid Health Check', - 'type': 'fatal' - } - ] - } + expected_payload = {"errors": [{"code": "health check failed", "message": "Invalid Health Check", "type": "fatal"}]} assert response.status_code == HTTPStatus.OK assert response.get_json() == expected_payload diff --git a/code/tests/unit/api/utils.py b/code/tests/unit/api/utils.py index 913e7b1..186905e 100644 --- a/code/tests/unit/api/utils.py +++ b/code/tests/unit/api/utils.py @@ -1,2 +1,2 @@ -def get_headers(jwt, auth_type='Bearer'): - return {'Authorization': f'{auth_type} {jwt}'} +def get_headers(jwt, auth_type="Bearer"): + return {"Authorization": f"{auth_type} {jwt}"} diff --git a/code/tests/unit/conftest.py b/code/tests/unit/conftest.py index d9cd68b..7be1fec 100644 --- a/code/tests/unit/conftest.py +++ b/code/tests/unit/conftest.py @@ -9,7 +9,7 @@ from tests.unit.payloads_for_tests import PRIVATE_KEY -@fixture(scope='session') +@fixture(scope="session") def client(): app.rsa_private_key = PRIVATE_KEY @@ -19,52 +19,41 @@ def client(): yield client -@fixture(scope='session') +@fixture(scope="session") def valid_jwt(client): def _make_jwt( - url='test_host.com', - username='username', - password='password', - jwks_host='visibility.amp.cisco.com', - aud='http://localhost', - kid='02B1174234C29F8EFB69911438F597FF3FFEE6B7', - wrong_structure=False, - wrong_jwks_host=False, + url="test_host.com", + username="username", + password="password", + jwks_host="visibility.amp.cisco.com", + aud="http://localhost", + kid="02B1174234C29F8EFB69911438F597FF3FFEE6B7", + wrong_structure=False, + wrong_jwks_host=False, ): payload = { - 'url': url, - 'username': username, - 'password': password, - 'jwks_host': jwks_host, - 'aud': aud, + "url": url, + "username": username, + "password": password, + "jwks_host": jwks_host, + "aud": aud, } if wrong_jwks_host: - payload.pop('jwks_host') + payload.pop("jwks_host") if wrong_structure: - payload.pop('username') + payload.pop("username") - return jwt.encode( - payload, client.application.rsa_private_key, algorithm='RS256', - headers={ - 'kid': kid - } - ) + return jwt.encode(payload, client.application.rsa_private_key, algorithm="RS256", headers={"kid": kid}) return _make_jwt -@fixture(scope='module') +@fixture(scope="module") def invalid_json_expected_payload(): def _make_message(message): - return { - 'errors': [{ - 'code': INVALID_ARGUMENT, - 'message': message, - 'type': 'fatal' - }] - } + return {"errors": [{"code": INVALID_ARGUMENT, "message": message, "type": "fatal"}]} return _make_message diff --git a/code/tests/unit/payloads_for_tests.py b/code/tests/unit/payloads_for_tests.py index 8a1093d..61be852 100644 --- a/code/tests/unit/payloads_for_tests.py +++ b/code/tests/unit/payloads_for_tests.py @@ -344,10 +344,13 @@ { 'confidence': 'High', 'count': 2, - 'description': 'RSA Netwitness session ID 20153218 retrie' - 'ved from decoder nwpackets related to 52.' - '3.199.104', - 'id': 'sighting-63b847c9-4429-4328-804a-5a06c2d03f9f', + 'description': 'RSA Netwitness session ID 20153218 retrieved from decoder nwpackets related to 52.3.199.104' + '\n\nNetWitness Mapped Values:\n' + '- time: 2022-03-07T23:37:55.000000Z\n' + '- sessionid: 20153218\n' + '- did: nwpackets\n' + '- packets: 2\n\n\n', + 'id': 'transient:sighting-63b847c9-4429-4328-804a-5a06c2d03f9f', 'observables': [ {'type': 'ip', 'value': '52.3.199.104'} ], @@ -357,7 +360,7 @@ }, 'relations': [], 'schema_version': '1.1.4', - 'short_description': 'RSA Netwitness session ID 20153218', + 'short_description': 'RSA Netwitness session ID 20153218 (None)', 'source': 'RSA NetWitness', 'targets': [], 'type': 'sighting' @@ -365,10 +368,13 @@ { 'confidence': 'High', 'count': 2, - 'description': 'RSA Netwitness session ID 20153219 retrie' - 'ved from decoder nwpackets related to 52.' - '3.199.104', - 'id': 'sighting-c54d2bfa-a710-42af-92f2-f3a628b89c15', + 'description': 'RSA Netwitness session ID 20153219 retrieved from decoder nwpackets related to 52.3.199.104' + '\n\nNetWitness Mapped Values:\n' + '- time: 2022-03-07T23:37:55.000000Z\n' + '- sessionid: 20153219\n' + '- did: nwpackets\n' + '- packets: 2\n\n\n', + 'id': 'transient:sighting-c54d2bfa-a710-42af-92f2-f3a628b89c15', 'observables': [ {'type': 'ip', 'value': '52.3.199.104'} ], @@ -378,7 +384,7 @@ }, 'relations': [], 'schema_version': '1.1.4', - 'short_description': 'RSA Netwitness session ID 20153219', + 'short_description': 'RSA Netwitness session ID 20153219 (None)', 'source': 'RSA NetWitness', 'targets': [], 'type': 'sighting' @@ -386,10 +392,13 @@ { 'confidence': 'High', 'count': 2, - 'description': 'RSA Netwitness session ID 20122343 retrie' - 'ved from decoder nwpackets related to 52.' - '3.199.104', - 'id': 'sighting-806b4eb2-18da-4322-bf7d-3701823a87e5', + 'description': 'RSA Netwitness session ID 20122343 retrieved from decoder nwpackets related to 52.3.199.104' + '\n\nNetWitness Mapped Values:\n' + '- time: 2022-03-07T22:48:36.000000Z\n' + '- sessionid: 20122343\n' + '- did: nwpackets\n' + '- packets: 2\n\n\n', + 'id': 'transient:sighting-806b4eb2-18da-4322-bf7d-3701823a87e5', 'observables': [{'type': 'ip', 'value': '52.3.199.104'}], 'observed_time': { 'end_time': '2022-03-07T22:48:36.000000Z', @@ -397,7 +406,7 @@ }, 'relations': [], 'schema_version': '1.1.4', - 'short_description': 'RSA Netwitness session ID 20122343', + 'short_description': 'RSA Netwitness session ID 20122343 (None)', 'source': 'RSA NetWitness', 'targets': [], 'type': 'sighting' @@ -405,10 +414,13 @@ { 'confidence': 'High', 'count': 3, - 'description': 'RSA Netwitness session ID 20122344 retrie' - 'ved from decoder nwpackets related to 52.' - '3.199.104', - 'id': 'sighting-da992d05-3f6e-4433-89cd-67208df067c8', + 'description': 'RSA Netwitness session ID 20122344 retrieved from decoder nwpackets related to 52.3.199.104' + '\n\nNetWitness Mapped Values:\n' + '- time: 2022-03-07T22:48:36.000000Z\n' + '- sessionid: 20122344\n' + '- did: nwpackets\n' + '- packets: 3\n\n\n', + 'id': 'transient:sighting-da992d05-3f6e-4433-89cd-67208df067c8', 'observables': [{'type': 'ip', 'value': '52.3.199.104'}], 'observed_time': { 'end_time': '2022-03-07T22:48:36.000000Z', @@ -416,7 +428,7 @@ }, 'relations': [], 'schema_version': '1.1.4', - 'short_description': 'RSA Netwitness session ID 20122344', + 'short_description': 'RSA Netwitness session ID 20122344 (None)', 'source': 'RSA NetWitness', 'targets': [], 'type': 'sighting' @@ -424,10 +436,12 @@ { 'confidence': 'High', 'count': 1, - 'description': 'RSA Netwitness session ID 19934075 retrie' - 'ved from decoder nwpackets related to 52.' - '3.199.104', - 'id': 'sighting-78a6ba52-4353-43b2-9308-b5886f387682', + 'description': 'RSA Netwitness session ID 19934075 retrieved from decoder nwpackets related to 52.3.199.104' + '\n\nNetWitness Mapped Values:\n' + '- time: 2022-03-07T17:48:22.000000Z\n' + '- sessionid: 19934075\n' + '- did: nwpackets\n\n\n', + 'id': 'transient:sighting-78a6ba52-4353-43b2-9308-b5886f387682', 'observables': [{'type': 'ip', 'value': '52.3.199.104'}], 'observed_time': { 'end_time': '2022-03-07T17:48:22.000000Z', @@ -435,7 +449,7 @@ }, 'relations': [], 'schema_version': '1.1.4', - 'short_description': 'RSA Netwitness session ID 19934075', + 'short_description': 'RSA Netwitness session ID 19934075 (None)', 'source': 'RSA NetWitness', 'targets': [], 'type': 'sighting' diff --git a/code/tests/unit/test_app.py b/code/tests/unit/test_app.py index 1059440..21e5f02 100644 --- a/code/tests/unit/test_app.py +++ b/code/tests/unit/test_app.py @@ -4,22 +4,20 @@ from pytest import fixture -Call = namedtuple('Call', ('method', 'route', 'expected_status_code')) +Call = namedtuple("Call", ("method", "route", "expected_status_code")) def calls(): - yield Call('POST', '/post', HTTPStatus.NOT_FOUND) - yield Call('GET', '/get', HTTPStatus.NOT_FOUND) - yield Call('PUT', '/put', HTTPStatus.NOT_FOUND) - yield Call('DELETE', '/delete', HTTPStatus.NOT_FOUND) + yield Call("POST", "/post", HTTPStatus.NOT_FOUND) + yield Call("GET", "/get", HTTPStatus.NOT_FOUND) + yield Call("PUT", "/put", HTTPStatus.NOT_FOUND) + yield Call("DELETE", "/delete", HTTPStatus.NOT_FOUND) - yield Call('GET', '/version', HTTPStatus.METHOD_NOT_ALLOWED) - yield Call('GET', '/observe/observables', HTTPStatus.METHOD_NOT_ALLOWED) + yield Call("GET", "/version", HTTPStatus.METHOD_NOT_ALLOWED) + yield Call("GET", "/observe/observables", HTTPStatus.METHOD_NOT_ALLOWED) -@fixture(scope='module', - params=calls(), - ids=lambda call: f'{call.method} {call.route}') +@fixture(scope="module", params=calls(), ids=lambda call: f"{call.method} {call.route}") def call(request): return request.param diff --git a/module_type.json.sample b/module_type.sample.json similarity index 100% rename from module_type.json.sample rename to module_type.sample.json