Path: blob/master/tests/test_validate_targets.py
761 views
import pytest1import re2import rstr34from sherlock_project.sherlock import sherlock5from sherlock_project.notify import QueryNotify6from sherlock_project.result import QueryResult, QueryStatus789FALSE_POSITIVE_ATTEMPTS: int = 2 # Since the usernames are randomly generated, it's POSSIBLE that a real username can be hit10FALSE_POSITIVE_QUANTIFIER_UPPER_BOUND: int = 15 # If a pattern uses quantifiers such as `+` `*` or `{n,}`, limit the upper bound (0 to disable)11FALSE_POSITIVE_DEFAULT_PATTERN: str = r'^[a-zA-Z0-9]{7,20}$' # Used in absence of a regexCheck entry121314def set_pattern_upper_bound(pattern: str, upper_bound: int = FALSE_POSITIVE_QUANTIFIER_UPPER_BOUND) -> str:15"""Set upper bound for regex patterns that use quantifiers such as `+` `*` or `{n,}`."""16def replace_upper_bound(match: re.Match) -> str: # type: ignore17lower_bound: int = int(match.group(1)) if match.group(1) else 0 # type: ignore18nonlocal upper_bound19upper_bound = upper_bound if lower_bound < upper_bound else lower_bound # type: ignore # noqa: F82320return f'{{{lower_bound},{upper_bound}}}'2122pattern = re.sub(r'(?<!\\)\{(\d+),\}', replace_upper_bound, pattern) # {n,} # type: ignore23pattern = re.sub(r'(?<!\\)\+', f'{{1,{upper_bound}}}', pattern) # +24pattern = re.sub(r'(?<!\\)\*', f'{{0,{upper_bound}}}', pattern) # *2526return pattern2728def false_positive_check(sites_info: dict[str, dict[str, str]], site: str, pattern: str) -> QueryStatus:29"""Check if a site is likely to produce false positives."""30status: QueryStatus = QueryStatus.UNKNOWN3132for _ in range(FALSE_POSITIVE_ATTEMPTS):33query_notify: QueryNotify = QueryNotify()34username: str = rstr.xeger(pattern)3536result: QueryResult | str = sherlock(37username=username,38site_data=sites_info,39query_notify=query_notify,40)[site]['status']4142if not hasattr(result, 'status'):43raise TypeError(f"Result for site {site} does not have 'status' attribute. Actual result: {result}")44if type(result.status) is not QueryStatus: # type: ignore45raise TypeError(f"Result status for site {site} is not of type QueryStatus. Actual type: {type(result.status)}") # type: ignore46status = result.status # type: ignore4748if status in (QueryStatus.AVAILABLE, QueryStatus.WAF):49return status5051return status525354def false_negative_check(sites_info: dict[str, dict[str, str]], site: str) -> QueryStatus:55"""Check if a site is likely to produce false negatives."""56status: QueryStatus = QueryStatus.UNKNOWN57query_notify: QueryNotify = QueryNotify()5859result: QueryResult | str = sherlock(60username=sites_info[site]['username_claimed'],61site_data=sites_info,62query_notify=query_notify,63)[site]['status']6465if not hasattr(result, 'status'):66raise TypeError(f"Result for site {site} does not have 'status' attribute. Actual result: {result}")67if type(result.status) is not QueryStatus: # type: ignore68raise TypeError(f"Result status for site {site} is not of type QueryStatus. Actual type: {type(result.status)}") # type: ignore69status = result.status # type: ignore7071return status7273@pytest.mark.validate_targets74@pytest.mark.online75class Test_All_Targets:7677@pytest.mark.validate_targets_fp78def test_false_pos(self, chunked_sites: dict[str, dict[str, str]]):79"""Iterate through all sites in the manifest to discover possible false-positive inducting targets."""80pattern: str81for site in chunked_sites:82try:83pattern = chunked_sites[site]['regexCheck']84except KeyError:85pattern = FALSE_POSITIVE_DEFAULT_PATTERN8687if FALSE_POSITIVE_QUANTIFIER_UPPER_BOUND > 0:88pattern = set_pattern_upper_bound(pattern)8990result: QueryStatus = false_positive_check(chunked_sites, site, pattern)91assert result is QueryStatus.AVAILABLE, f"{site} produced false positive with pattern {pattern}, result was {result}"9293@pytest.mark.validate_targets_fn94def test_false_neg(self, chunked_sites: dict[str, dict[str, str]]):95"""Iterate through all sites in the manifest to discover possible false-negative inducting targets."""96for site in chunked_sites:97result: QueryStatus = false_negative_check(chunked_sites, site)98assert result is QueryStatus.CLAIMED, f"{site} produced false negative, result was {result}"99100101102