Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter configurable arguments in rules config #622

Merged
merged 4 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .ci/benchmark.txt
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,12 @@ FileType FileNumber ValidLines Positives Negatives Templat
.zsh 6 872 12
.zsh-theme 1 97 1
TOTAL: 10232 16342283 12255 49690 5102
credsweeper result_cnt : 11521, lost_cnt : 0, true_cnt : 11342, false_cnt : 179
credsweeper result_cnt : 11517, lost_cnt : 0, true_cnt : 11342, false_cnt : 175
Rules Positives Negatives Templates Reported TP FP TN FN FPR FNR ACC PRC RCL F1
------------------------------ ----------- ----------- ----------- ---------- ----- ---- ----- ---- -------- -------- -------- -------- -------- --------
API 130 3166 188 125 123 2 3352 7 0.000596 0.053846 0.997417 0.984000 0.946154 0.964706
AWS Client ID 168 21 0 160 160 0 21 8 0.000000 0.047619 0.957672 1.000000 0.952381 0.975610
AWS Multi 82 10 0 88 82 5 5 0 0.500000 0.000000 0.945652 0.942529 1.000000 0.970414
AWS Multi 82 10 0 84 82 1 9 0 0.100000 0.000000 0.989130 0.987952 1.000000 0.993939
AWS S3 Bucket 67 23 0 92 67 23 0 0 1.000000 0.000000 0.744444 0.744444 1.000000 0.853503
Atlassian Old PAT token 27 308 3 12 3 8 303 24 0.025723 0.888889 0.905325 0.272727 0.111111 0.157895
Auth 414 2739 82 390 387 3 2818 27 0.001063 0.065217 0.990726 0.992308 0.934783 0.962687
Expand Down Expand Up @@ -271,4 +271,4 @@ Token 643 4170 454 61
Twilio Credentials 30 39 0 30 30 0 39 0 0.000000 0.000000 1.000000 1.000000 1.000000 1.000000
URL Credentials 210 156 216 205 205 0 372 5 0.000000 0.023810 0.991409 1.000000 0.976190 0.987952
UUID 1069 265 0 1068 1067 1 264 2 0.003774 0.001871 0.997751 0.999064 0.998129 0.998596
12255 49690 5102 11528 11342 179 49511 913 0.003602 0.074500 0.982371 0.984463 0.925500 0.954071
12255 49690 5102 11524 11342 175 49515 913 0.003522 0.074500 0.982436 0.984805 0.925500 0.954232
2 changes: 1 addition & 1 deletion credsweeper/filters/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Filter:
"""Base class for all filters that operates on 'line_data' objects."""

@abstractmethod
def __init__(self, config: Config):
def __init__(self, config: Config, *args):
raise NotImplementedError()

@abstractmethod
Expand Down
7 changes: 4 additions & 3 deletions credsweeper/filters/value_couple_keyword_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
class ValueCoupleKeywordCheck(Filter):
"""Check value if TWO words from morphemes checklist exists in value"""

def __init__(self, config: Config = None) -> None:
pass
def __init__(self, config: Config = None, threshold=1) -> None:
# threshold - minimum morphemes number in a value
self.threshold = threshold

def run(self, line_data: LineData, target: AnalysisTarget) -> bool:
"""Run filter checks on received credential candidate data 'line_data'.
Expand All @@ -22,4 +23,4 @@ def run(self, line_data: LineData, target: AnalysisTarget) -> bool:
True, if need to filter candidate and False if left

"""
return static_keyword_checklist.check_morphemes(line_data.value.lower(), 1)
return static_keyword_checklist.check_morphemes(line_data.value.lower(), self.threshold)
6 changes: 5 additions & 1 deletion credsweeper/rules/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,10 @@
values:
- (?<![0-9A-Za-z_-])(?P<value>(ABIA|ACCA|AGPA|AIDA|AIPA|AKIA|ANPA|ANVA|AROA|APKA|ASCA|ASIA)[0-9A-Z]{16,17})(?![0-9A-Za-z_-])
- (?<![0-9A-Za-z_/+-])(?P<value>[0-9A-Za-z/+]{35,80})(?![0-9A-Za-z_/+-])
filter_type: GeneralPattern
filter_type:
- LineSpecificKeyCheck
- ValuePatternCheck
- ValueCoupleKeywordCheck(3)
required_substrings:
- A
min_line_len: 20
Expand Down Expand Up @@ -1040,6 +1043,7 @@
- (?<![0-9A-Za-z_-])(?P<value>[NMO][ADgjQTwz][0-9A-Za-z_-]{42})(?![0-9A-Za-z_-])
filter_type:
- ValueAtlassianTokenCheck
- ValueBase64PartCheck
min_line_len: 44
required_substrings:
- M
Expand Down
24 changes: 22 additions & 2 deletions credsweeper/rules/rule.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import logging
import re
from functools import cached_property
Expand Down Expand Up @@ -112,6 +113,15 @@ def filters(self) -> List[Filter]:
"""filters getter"""
return self.__filters

@staticmethod
def _get_arg(arg: str) -> Union[int, float, str]:
"""Transform given string value to int, then float. In worst case - returns str"""
with contextlib.suppress(Exception):
return int(arg)
with contextlib.suppress(Exception):
return float(arg)
return str(arg)

def _init_filters(self, filter_type: Union[None, str, List[str]]) -> List[Filter]:
"""
filter_type: str - applies Group of filter
Expand All @@ -126,9 +136,19 @@ def _init_filters(self, filter_type: Union[None, str, List[str]]) -> List[Filter
elif isinstance(filter_type, list):
# list type means - list of (Filter)s is applied
for i in filter_type:
_filter = getattr(filters, i, None)
if '(' in i and ')' in i:
left_pos = i.find('(')
filter_parameters = [self._get_arg(x.strip()) for x in i[left_pos + 1:i.find(')')].split(',')]
filter_name = i[:left_pos].strip()
else:
filter_parameters = None
filter_name = i
_filter = getattr(filters, filter_name, None)
if isinstance(_filter, type) and issubclass(_filter, Filter):
_filters.append(_filter(self.config))
if filter_parameters:
_filters.append(_filter(self.config, *filter_parameters))
else:
_filters.append(_filter(self.config))
else:
break
else:
Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
SAMPLES_IN_DOC = 447

# archived credentials that are not found without --depth
SAMPLES_IN_DEEP_1 = SAMPLES_POST_CRED_COUNT + 30
SAMPLES_IN_DEEP_1 = SAMPLES_POST_CRED_COUNT + 29
SAMPLES_IN_DEEP_2 = SAMPLES_IN_DEEP_1 + 53
SAMPLES_IN_DEEP_3 = SAMPLES_IN_DEEP_2 + 1

Expand Down
50 changes: 3 additions & 47 deletions tests/data/depth_3.json
Original file line number Diff line number Diff line change
Expand Up @@ -580,50 +580,6 @@
}
]
},
{
"api_validation": "NOT_AVAILABLE",
"ml_validation": "NOT_AVAILABLE",
"ml_probability": null,
"rule": "AWS Multi",
"severity": "high",
"confidence": "moderate",
"line_data_list": [
{
"line": "<p:sld xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:p14=\"http://schemas.microsoft.com/office/powerpoint/2010/main\" xmlns:p15=\"http://schemas.microsoft.com/office/powerpoint/2012/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"><p:cSld><p:spTree><p:nvGrpSpPr> <p:cNvPr id=\"1\" name=\"\"/> <p:cNvGrpSpPr/> <p:nvPr/> </p:nvGrpSpPr> <p:grpSpPr> <a:xfrm> <a:off x=\"0\" y=\"0\"/> <a:ext cx=\"0\" cy=\"0\"/> <a:chOff x=\"0\" y=\"0\"/> <a:chExt cx=\"0\" cy=\"0\"/> </a:xfrm> </p:grpSpPr><p:sp><p:nvSpPr><p:cNvPr id=\"6\" name=\"PlaceHolder 1\"></p:cNvPr><p:cNvSpPr><a:spLocks noGrp=\"1\"/></p:cNvSpPr><p:nvPr><p:ph type=\"title\"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x=\"504000\" y=\"190080\"/><a:ext cx=\"9097200\" cy=\"1410120\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom><a:noFill/><a:ln w=\"0\"><a:noFill/></a:ln></p:spPr><p:txBody><a:bodyPr lIns=\"0\" rIns=\"0\" tIns=\"0\" bIns=\"0\" anchor=\"ctr\"><a:noAutofit/></a:bodyPr><a:p><a:pPr indent=\"0\" algn=\"ctr\"><a:buNone/></a:pPr><a:r><a:rPr b=\"0\" lang=\"en-US\" sz=\"4400\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"000000\"/></a:solidFill><a:latin typeface=\"Arial\"/></a:rPr><a:t>Follow the white rabbit</a:t></a:r><a:endParaRPr b=\"0\" lang=\"en-US\" sz=\"4400\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"000000\"/></a:solidFill><a:latin typeface=\"Arial\"/></a:endParaRPr></a:p></p:txBody></p:sp><p:sp><p:nvSpPr><p:cNvPr id=\"7\" name=\"PlaceHolder 2\"></p:cNvPr><p:cNvSpPr><a:spLocks noGrp=\"1\"/></p:cNvSpPr><p:nvPr><p:ph type=\"subTitle\"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x=\"504000\" y=\"2286000\"/><a:ext cx=\"9071280\" cy=\"2328480\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom><a:noFill/><a:ln w=\"0\"><a:noFill/></a:ln></p:spPr><p:txBody><a:bodyPr lIns=\"0\" rIns=\"0\" tIns=\"0\" bIns=\"0\" anchor=\"ctr\"><a:noAutofit/></a:bodyPr><a:p><a:pPr indent=\"0\" algn=\"ctr\"><a:lnSpc><a:spcPct val=\"100000\"/></a:lnSpc><a:buNone/><a:tabLst><a:tab algn=\"l\" pos=\"0\"/></a:tabLst></a:pPr><a:r><a:rPr b=\"0\" lang=\"en-US\" sz=\"1000\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"ffffff\"/></a:solidFill><a:latin typeface=\"JetBrains Mono\"/><a:ea typeface=\"JetBrains Mono\"/></a:rPr><a:t>AKIAGIREOGIPPTX1Y45X</a:t></a:r><a:endParaRPr b=\"0\" lang=\"en-US\" sz=\"1000\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"ffffff\"/></a:solidFill><a:latin typeface=\"Arial\"/></a:endParaRPr></a:p></p:txBody></p:sp></p:spTree></p:cSld><mc:AlternateContent><mc:Choice Requires=\"p14\"><p:transition spd=\"slow\" p14:dur=\"2000\"></p:transition></mc:Choice><mc:Fallback><p:transition spd=\"slow\"></p:transition></mc:Fallback></mc:AlternateContent></p:sld>",
"line_num": 2,
"path": "./tests/samples/aws_id.pptx",
"info": "./tests/samples/aws_id.pptx|ZIP|ppt/slides/slide1.xml|RAW",
"value": "AKIAGIREOGIPPTX1Y45X",
"value_start": 2403,
"value_end": 2423,
"variable": null,
"variable_start": -2,
"variable_end": -2,
"entropy_validation": {
"iterator": "BASE64_CHARS",
"entropy": 3.6841837197791887,
"valid": false
}
},
{
"line": "<p:sld xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:p=\"http://schemas.openxmlformats.org/presentationml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\" xmlns:p14=\"http://schemas.microsoft.com/office/powerpoint/2010/main\" xmlns:p15=\"http://schemas.microsoft.com/office/powerpoint/2012/main\" xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"><p:cSld><p:spTree><p:nvGrpSpPr> <p:cNvPr id=\"1\" name=\"\"/> <p:cNvGrpSpPr/> <p:nvPr/> </p:nvGrpSpPr> <p:grpSpPr> <a:xfrm> <a:off x=\"0\" y=\"0\"/> <a:ext cx=\"0\" cy=\"0\"/> <a:chOff x=\"0\" y=\"0\"/> <a:chExt cx=\"0\" cy=\"0\"/> </a:xfrm> </p:grpSpPr><p:sp><p:nvSpPr><p:cNvPr id=\"6\" name=\"PlaceHolder 1\"></p:cNvPr><p:cNvSpPr><a:spLocks noGrp=\"1\"/></p:cNvSpPr><p:nvPr><p:ph type=\"title\"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x=\"504000\" y=\"190080\"/><a:ext cx=\"9097200\" cy=\"1410120\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom><a:noFill/><a:ln w=\"0\"><a:noFill/></a:ln></p:spPr><p:txBody><a:bodyPr lIns=\"0\" rIns=\"0\" tIns=\"0\" bIns=\"0\" anchor=\"ctr\"><a:noAutofit/></a:bodyPr><a:p><a:pPr indent=\"0\" algn=\"ctr\"><a:buNone/></a:pPr><a:r><a:rPr b=\"0\" lang=\"en-US\" sz=\"4400\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"000000\"/></a:solidFill><a:latin typeface=\"Arial\"/></a:rPr><a:t>Follow the white rabbit</a:t></a:r><a:endParaRPr b=\"0\" lang=\"en-US\" sz=\"4400\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"000000\"/></a:solidFill><a:latin typeface=\"Arial\"/></a:endParaRPr></a:p></p:txBody></p:sp><p:sp><p:nvSpPr><p:cNvPr id=\"7\" name=\"PlaceHolder 2\"></p:cNvPr><p:cNvSpPr><a:spLocks noGrp=\"1\"/></p:cNvSpPr><p:nvPr><p:ph type=\"subTitle\"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x=\"504000\" y=\"2286000\"/><a:ext cx=\"9071280\" cy=\"2328480\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom><a:noFill/><a:ln w=\"0\"><a:noFill/></a:ln></p:spPr><p:txBody><a:bodyPr lIns=\"0\" rIns=\"0\" tIns=\"0\" bIns=\"0\" anchor=\"ctr\"><a:noAutofit/></a:bodyPr><a:p><a:pPr indent=\"0\" algn=\"ctr\"><a:lnSpc><a:spcPct val=\"100000\"/></a:lnSpc><a:buNone/><a:tabLst><a:tab algn=\"l\" pos=\"0\"/></a:tabLst></a:pPr><a:r><a:rPr b=\"0\" lang=\"en-US\" sz=\"1000\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"ffffff\"/></a:solidFill><a:latin typeface=\"JetBrains Mono\"/><a:ea typeface=\"JetBrains Mono\"/></a:rPr><a:t>AKIAGIREOGIPPTX1Y45X</a:t></a:r><a:endParaRPr b=\"0\" lang=\"en-US\" sz=\"1000\" spc=\"-1\" strike=\"noStrike\"><a:solidFill><a:srgbClr val=\"ffffff\"/></a:solidFill><a:latin typeface=\"Arial\"/></a:endParaRPr></a:p></p:txBody></p:sp></p:spTree></p:cSld><mc:AlternateContent><mc:Choice Requires=\"p14\"><p:transition spd=\"slow\" p14:dur=\"2000\"></p:transition></mc:Choice><mc:Fallback><p:transition spd=\"slow\"></p:transition></mc:Fallback></mc:AlternateContent></p:sld>",
"line_num": 2,
"path": "./tests/samples/aws_id.pptx",
"info": "./tests/samples/aws_id.pptx|ZIP|ppt/slides/slide1.xml|RAW",
"value": "org/officeDocument/2006/relationships",
"value_start": 179,
"value_end": 216,
"variable": null,
"variable_start": -2,
"variable_end": -2,
"entropy_validation": {
"iterator": "BASE36_CHARS",
"entropy": 3.794653677335903,
"valid": true
}
}
]
},
{
"api_validation": "NOT_AVAILABLE",
"ml_validation": "NOT_AVAILABLE",
Expand Down Expand Up @@ -8143,13 +8099,13 @@
"confidence": "strong",
"line_data_list": [
{
"line": "JIRA = \"OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ\"",
"line": "TP: https://www.example.com/api/verification/version2322/token/OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ",
"line_num": 1,
"path": "./tests/samples/jira_confluence_pat",
"info": "./tests/samples/jira_confluence_pat|RAW",
"value": "OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ",
"value_start": 8,
"value_end": 52,
"value_start": 63,
"value_end": 107,
"variable": null,
"variable_start": -2,
"variable_end": -2,
Expand Down
6 changes: 3 additions & 3 deletions tests/data/doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12228,13 +12228,13 @@
"confidence": "strong",
"line_data_list": [
{
"line": "JIRA = \"OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ\"",
"line": "TP: https://www.example.com/api/verification/version2322/token/OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ",
"line_num": 1,
"path": "./tests/samples/jira_confluence_pat",
"info": "./tests/samples/jira_confluence_pat|RAW",
"value": "OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ",
"value_start": 8,
"value_end": 52,
"value_start": 63,
"value_end": 107,
"variable": null,
"variable_start": -2,
"variable_end": -2,
Expand Down
6 changes: 3 additions & 3 deletions tests/data/ml_threshold.json
Original file line number Diff line number Diff line change
Expand Up @@ -8733,13 +8733,13 @@
"confidence": "strong",
"line_data_list": [
{
"line": "d29b86517de7225ce062bc602beb5886d1454a5255911f4c3ab140af6973cf8a",
"line": "1623edb467cc32623f35eae6186382b1b4c3c2f6d70e20f42d5ab342d5c79d03",
"line_num": 1,
"path": "./tests/samples/jira_confluence_pat",
"info": "",
"value": "728e0e362437be53ffa2f9ee605f3870000122d7d03c20ce3d7c7b8f1d733d8e",
"value_start": 8,
"value_end": 52,
"value_start": 63,
"value_end": 107,
"variable": null,
"variable_start": -2,
"variable_end": -2,
Expand Down
6 changes: 3 additions & 3 deletions tests/data/output.json
Original file line number Diff line number Diff line change
Expand Up @@ -7680,13 +7680,13 @@
"confidence": "strong",
"line_data_list": [
{
"line": "JIRA = \"OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ\"",
"line": "TP: https://www.example.com/api/verification/version2322/token/OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ",
"line_num": 1,
"path": "./tests/samples/jira_confluence_pat",
"info": "",
"value": "OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ",
"value_start": 8,
"value_end": 52,
"value_start": 63,
"value_end": 107,
"variable": null,
"variable_start": -2,
"variable_end": -2,
Expand Down
11 changes: 11 additions & 0 deletions tests/filters/test_value_couple_keyword_check.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

from credsweeper.filters import ValueCoupleKeywordCheck
from tests import AZ_STRING
from tests.filters.conftest import LINE_VALUE_PATTERN, DUMMY_ANALYSIS_TARGET
from tests.test_utils.dummy_line_data import get_line_data

Expand All @@ -18,3 +19,13 @@ def test_value_couple_keyword_check_p(self, file_path: pytest.fixture, line: str
def test_value_couple_keyword_check_n(self, file_path: pytest.fixture, line: str) -> None:
line_data = get_line_data(file_path, line=line, pattern=LINE_VALUE_PATTERN)
assert ValueCoupleKeywordCheck().run(line_data, DUMMY_ANALYSIS_TARGET) is True

@pytest.mark.parametrize("line", [AZ_STRING])
def test_value_couple_keyword_check_arg_n(self, file_path: pytest.fixture, line: str) -> None:
line_data = get_line_data(file_path, line=line, pattern=LINE_VALUE_PATTERN)
assert ValueCoupleKeywordCheck(threshold=9).run(line_data, DUMMY_ANALYSIS_TARGET) is False

@pytest.mark.parametrize("line", [AZ_STRING])
def test_value_couple_keyword_check_arg_p(self, file_path: pytest.fixture, line: str) -> None:
line_data = get_line_data(file_path, line=line, pattern=LINE_VALUE_PATTERN)
assert ValueCoupleKeywordCheck(threshold=8).run(line_data, DUMMY_ANALYSIS_TARGET) is True
2 changes: 2 additions & 0 deletions tests/samples/aws_client_id
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ The items are AKIAGIREOGIAWSKEY123,AKIAGIREOGIAWSKEY45X
the coma is necessary there ^ bariers thesting !!!
must be filtered: AKIAGIREOGIAEXAMPLE7
filtered too: AKIALGSBKLIKEAREAL12 --access-key <xcFsdeGddSAdI/KFRS2CB/3fGCsdCYEXAMPLEKEY>

exchangeAuthorizationAccessTokenWithPermission # looks like a key, but FP for aws multi
3 changes: 2 additions & 1 deletion tests/samples/jira_confluence_pat
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
JIRA = "OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ"
TP: https://www.example.com/api/verification/version2322/token/OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ
FP: "image/png": "iVBORx09VIskhxhCe7sh03R1dnENPiB66xQSIZjEYN13vafX/OTI2NjA3NjU1NTI2Oh2DOnASdOHoIhEGyqIuYrdkYaQZ/hZwUteHsmN+z+aoEAAAAvL+Q5FSQGyqIuYrdkYaQZuW1TvI=\n",
Loading