diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index df59ca7..89cd9dc 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -22,4 +22,6 @@ jobs: - name: Slack EG Tests run: python -m unittest tests/test_format_slack_eg.py - name: Slack STD Tests - run: python -m unittest tests/test_format_slack_std.py \ No newline at end of file + run: python -m unittest tests/test_format_slack_std.py + - name: Stack Overflow Tests + run: python -m unittest tests/test_format_stack_overflow.py \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d31942..12bd6c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2024-04-26 +### Added +- Signatures now support Stack Overflow Watchman + - Tests added for the new Stack Overflow Watchman format + ## 2023-12-22 ### Added - Added signatures for: diff --git a/models/signature_stack_overflow.py b/models/signature_stack_overflow.py new file mode 100644 index 0000000..a6c471c --- /dev/null +++ b/models/signature_stack_overflow.py @@ -0,0 +1,69 @@ +import pathlib +from dataclasses import dataclass + +import yaml + + +@dataclass(slots=True) +class Signature: + """ Class that handles loaded signature objects. Signatures + define what to search for in Stack Overflow and where to search for it. + They also contain regex patterns to validate data that is found""" + + name: str + status: bool + author: str + date: str + version: str + description: str + severity: int + watchman_apps: list + scope: list + test_cases: dataclass + search_strings: str + patterns: str + + +@dataclass(slots=True) +class TestCases: + match_cases: list + fail_cases: list + + +def load_from_yaml(sig_path: pathlib.PosixPath) -> list[Signature]: + """Load YAML file and return a Signature object + + Args: + sig_path: Path of YAML file + Returns: + Signature object with fields populated from the YAML + signature file + """ + + with open(sig_path) as yaml_file: + yaml_import = yaml.safe_load(yaml_file) + + output = [] + for sig in yaml_import.get('signatures'): + if 'stack_overflow' in sig.get('watchman_apps'): + output.append( + Signature( + name=sig.get('name'), + status=sig.get('status'), + author=sig.get('author'), + date=sig.get('date'), + version=sig.get('version'), + description=sig.get('description'), + severity=sig.get('severity'), + watchman_apps=sig.get('watchman_apps'), + scope=sig.get('watchman_apps').get('stack_overflow').get('scope'), + test_cases=TestCases( + match_cases=sig.get('test_cases').get('match_cases'), + fail_cases=sig.get('test_cases').get('fail_cases') + ), + search_strings=sig.get('watchman_apps').get('stack_overflow').get('search_strings'), + patterns=sig.get('patterns') + ) + ) + + return output \ No newline at end of file diff --git a/signatures/tokens_and_credentials/akamai.yaml b/signatures/tokens_and_credentials/akamai.yaml index 683fa36..7043b59 100644 --- a/signatures/tokens_and_credentials/akamai.yaml +++ b/signatures/tokens_and_credentials/akamai.yaml @@ -11,6 +11,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - akab- slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/alibaba.yaml b/signatures/tokens_and_credentials/alibaba.yaml index b74d737..f4d2fe8 100644 --- a/signatures/tokens_and_credentials/alibaba.yaml +++ b/signatures/tokens_and_credentials/alibaba.yaml @@ -1,6 +1,7 @@ --- filename: alibaba.yaml signatures: + - name: Alibaba IAM Access Key ID status: enabled author: PaperMtn @@ -10,6 +11,12 @@ signatures: notes: null references: null watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - LTAI slack_std: category: secrets scope: @@ -47,6 +54,7 @@ signatures: - accessKeyId=LAAIAAAZ5BhleEv7 patterns: - LTAI[0-9a-zA-Z]{12,20} + - name: Alibaba IAM Secret Access Key status: enabled author: PaperMtn @@ -56,6 +64,12 @@ signatures: notes: null references: null watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - LTAI slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/aws.yaml b/signatures/tokens_and_credentials/aws.yaml index 524028a..275515b 100644 --- a/signatures/tokens_and_credentials/aws.yaml +++ b/signatures/tokens_and_credentials/aws.yaml @@ -9,6 +9,14 @@ signatures: description: Detects exposed AWS API secret tokens severity: "90" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - aws_access_key_id + - aws_secret_access_key + - aws_session_token slack_std: category: secrets scope: @@ -57,6 +65,14 @@ signatures: description: Detects S3 bucket URLs, a potential source of exposed data severity: "30" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - s3.amazonaws.com + - s3.console.aws.amazon.com + - s3:// slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/azure.yaml b/signatures/tokens_and_credentials/azure.yaml index 31e7f00..2493a38 100644 --- a/signatures/tokens_and_credentials/azure.yaml +++ b/signatures/tokens_and_credentials/azure.yaml @@ -13,6 +13,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - ".cscfg" slack_std: category: secrets scope: @@ -53,6 +59,14 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - login.microsoftonline.com + - management.azure + - management.core slack_std: category: secrets scope: @@ -105,6 +119,15 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - azureProfile.json + - az.sess + - az.json + - clouds.configtelemetry.txt slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/cloudflare.yaml b/signatures/tokens_and_credentials/cloudflare.yaml index fc97f66..cbecaa5 100644 --- a/signatures/tokens_and_credentials/cloudflare.yaml +++ b/signatures/tokens_and_credentials/cloudflare.yaml @@ -11,6 +11,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.cloudflare.com + - cloudflare_ slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/facebook.yaml b/signatures/tokens_and_credentials/facebook.yaml index 1b49c87..c4f4bc3 100644 --- a/signatures/tokens_and_credentials/facebook.yaml +++ b/signatures/tokens_and_credentials/facebook.yaml @@ -11,6 +11,15 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - graph.facebook.com + - facebook.com/dialog/oauth + - eaaced + - client_secret slack_std: category: secrets scope: @@ -67,6 +76,15 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - graph.facebook.com + - facebook.com/dialog/oauth + - eaaced + - client_secret slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/ftp.yaml b/signatures/tokens_and_credentials/ftp.yaml index fb98c5d..0287556 100644 --- a/signatures/tokens_and_credentials/ftp.yaml +++ b/signatures/tokens_and_credentials/ftp.yaml @@ -9,6 +9,12 @@ signatures: description: Detects exposed FTP credentials severity: "90" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - ftp slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/generic_tokens.yaml b/signatures/tokens_and_credentials/generic_tokens.yaml index ba71663..1cd0a93 100644 --- a/signatures/tokens_and_credentials/generic_tokens.yaml +++ b/signatures/tokens_and_credentials/generic_tokens.yaml @@ -9,6 +9,12 @@ signatures: description: Detects exposed access_tokens severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - access_token slack_std: category: secrets scope: @@ -54,6 +60,12 @@ signatures: description: Detects exposed bearer tokens_and_credentials severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - bearer slack_std: category: secrets scope: @@ -101,6 +113,12 @@ signatures: description: Detects exposed client_secrets severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - client_secret slack_std: category: secrets scope: @@ -146,6 +164,12 @@ signatures: description: Detects exposed private_tokens severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - PRIVATE slack_std: category: secrets scope: @@ -181,6 +205,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - x-api-key slack_std: category: secrets scope: @@ -229,6 +259,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - x-auth-key slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/github.yaml b/signatures/tokens_and_credentials/github.yaml index c2ce14c..848d0fd 100644 --- a/signatures/tokens_and_credentials/github.yaml +++ b/signatures/tokens_and_credentials/github.yaml @@ -11,6 +11,14 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.github.com + - github.com/login/oauth/ + - github access_token slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/google.yaml b/signatures/tokens_and_credentials/google.yaml index 0af22ea..0ae96b4 100644 --- a/signatures/tokens_and_credentials/google.yaml +++ b/signatures/tokens_and_credentials/google.yaml @@ -9,6 +9,15 @@ signatures: description: Detects exposed API tokens for various Google services severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - googleapis.com + - google.com + - google + - googleapis slack_std: category: secrets scope: @@ -58,6 +67,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - iam.gserviceaccount.com slack_std: category: secrets scope: @@ -99,6 +114,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - iam.gserviceaccount.com + - "-----BEGIN PRIVATE KEY -----" slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/grafana.yaml b/signatures/tokens_and_credentials/grafana.yaml index 21e66b1..f589f85 100644 --- a/signatures/tokens_and_credentials/grafana.yaml +++ b/signatures/tokens_and_credentials/grafana.yaml @@ -11,6 +11,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - grafana slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/heroku.yaml b/signatures/tokens_and_credentials/heroku.yaml index ef287de..ff55681 100644 --- a/signatures/tokens_and_credentials/heroku.yaml +++ b/signatures/tokens_and_credentials/heroku.yaml @@ -11,6 +11,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.heroku.com + - HEROKU_API slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/mailchimp.yaml b/signatures/tokens_and_credentials/mailchimp.yaml index 46ebd95..9912a5a 100644 --- a/signatures/tokens_and_credentials/mailchimp.yaml +++ b/signatures/tokens_and_credentials/mailchimp.yaml @@ -11,6 +11,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.mailchimp.com + - MAILCHIMP_API slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/mailgun.yaml b/signatures/tokens_and_credentials/mailgun.yaml index 63f9da9..9bb1d7e 100644 --- a/signatures/tokens_and_credentials/mailgun.yaml +++ b/signatures/tokens_and_credentials/mailgun.yaml @@ -11,6 +11,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.mailgun.net + - MAILGUN_API slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/mastercard.yml b/signatures/tokens_and_credentials/mastercard.yml deleted file mode 100644 index 55ea172..0000000 --- a/signatures/tokens_and_credentials/mastercard.yml +++ /dev/null @@ -1,57 +0,0 @@ ---- -filename: mastercard.yaml -signatures: - - - name: MasterCard DataCash - status: enabled - author: PaperMtn - date: "2020-10-02" - description: Detects potentially exposed MasterCard DataCash credentials - severity: "70" - notes: - references: - watchman_apps: - slack_std: - category: secrets - scope: - - messages - file_types: - search_strings: - - "vTID" - - "datacash" - slack_eg: - scope: - - messages - - drafts - file_types: - locations: - - public - - private - - connect - search_strings: - - "vTID" - - "datacash" - gitlab: - scope: - - blobs - - commits - - milestones - - wiki_blobs - - issues - - merge_requests - - notes - - snippet_titles - search_strings: - - 'vTID -(svg|png|jpeg)' - - 'datacash -(svg|png|jpeg)' - test_cases: - match_cases: - - "99000000" - - "21000000" - - "88123123" - fail_cases: - - "90000000" - - "89000000" - patterns: - - "([99]{2}|[88]{2}|[21]{2})[0-9]{6}" - diff --git a/signatures/tokens_and_credentials/ms_teams.yaml b/signatures/tokens_and_credentials/ms_teams.yaml index c99b74f..06d4631 100644 --- a/signatures/tokens_and_credentials/ms_teams.yaml +++ b/signatures/tokens_and_credentials/ms_teams.yaml @@ -11,6 +11,13 @@ signatures: notes: references: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - outlook.office365.com + - outlook.office.com slack_std: category: secrets scope: @@ -61,6 +68,13 @@ signatures: notes: references: https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - webhook.office.com + - webhook.office.com slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/new_relic.yaml b/signatures/tokens_and_credentials/new_relic.yaml index 83ef4e8..fcdc55a 100644 --- a/signatures/tokens_and_credentials/new_relic.yaml +++ b/signatures/tokens_and_credentials/new_relic.yaml @@ -11,6 +11,14 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.newrelic.com + - relic + - 'X-Api-Key' slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/openai.yaml b/signatures/tokens_and_credentials/openai.yaml index ba05552..e4178cd 100644 --- a/signatures/tokens_and_credentials/openai.yaml +++ b/signatures/tokens_and_credentials/openai.yaml @@ -11,6 +11,14 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.openai.com + - openai + - sk- slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/pagerduty.yaml b/signatures/tokens_and_credentials/pagerduty.yaml index d0e65a3..6b20e26 100644 --- a/signatures/tokens_and_credentials/pagerduty.yaml +++ b/signatures/tokens_and_credentials/pagerduty.yaml @@ -9,6 +9,12 @@ signatures: description: Detects exposed PagerDuty API OAuth tokens severity: "90" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - pagerduty slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/passwords.yaml b/signatures/tokens_and_credentials/passwords.yaml index 897c842..7962e7f 100644 --- a/signatures/tokens_and_credentials/passwords.yaml +++ b/signatures/tokens_and_credentials/passwords.yaml @@ -11,6 +11,15 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - password + - pwd + - passwd + - pass slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/paypal.yaml b/signatures/tokens_and_credentials/paypal.yaml index 41bcbd3..1393f55 100644 --- a/signatures/tokens_and_credentials/paypal.yaml +++ b/signatures/tokens_and_credentials/paypal.yaml @@ -11,6 +11,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - paypal + - braintree slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/private_keys.yaml b/signatures/tokens_and_credentials/private_keys.yaml index 7c1252f..b6a36a5 100644 --- a/signatures/tokens_and_credentials/private_keys.yaml +++ b/signatures/tokens_and_credentials/private_keys.yaml @@ -9,6 +9,17 @@ signatures: description: Detects exposed PGP private keys severity: "90" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - "BEGIN PGP PRIVATE KEY BLOCK" + - "BEGIN RSA PRIVATE KEY" + - "BEGIN DSA PRIVATE" + - "BEGIN EC PRIVATE" + - "BEGIN OPENSSH PRIVATE" + - "BEGIN RSA PRIVATE" slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/shodan.yaml b/signatures/tokens_and_credentials/shodan.yaml index f8b01c5..5bd5a93 100644 --- a/signatures/tokens_and_credentials/shodan.yaml +++ b/signatures/tokens_and_credentials/shodan.yaml @@ -9,6 +9,12 @@ signatures: description: Detects exposed Shodan API tokens severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - shodan.io slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/slack.yaml b/signatures/tokens_and_credentials/slack.yaml index 2b9050a..1637f00 100644 --- a/signatures/tokens_and_credentials/slack.yaml +++ b/signatures/tokens_and_credentials/slack.yaml @@ -9,6 +9,18 @@ signatures: description: Detects exposed Slack API tokens severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.slack.com + - slack + - xoxb + - xoxa + - xoxp + - xoxr + - xoxs slack_std: category: secrets scope: @@ -66,6 +78,14 @@ signatures: description: Detects exposed Slack App tokens severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.slack.com + - slack + - xapp slack_std: category: secrets scope: @@ -111,6 +131,14 @@ signatures: description: Detects exposed Slack cookie values. These can be used for authentication severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.slack.com + - slack + - d= slack_std: category: secrets scope: @@ -156,6 +184,14 @@ signatures: description: Detects exposed Slack user session tokens severity: "70" watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.slack.com + - slack + - xoxs slack_std: category: secrets scope: @@ -203,6 +239,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - hooks.slack.com slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/snyk.yaml b/signatures/tokens_and_credentials/snyk.yaml index 173cb11..d086cb0 100644 --- a/signatures/tokens_and_credentials/snyk.yaml +++ b/signatures/tokens_and_credentials/snyk.yaml @@ -11,6 +11,12 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - snyk slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/stripe.yaml b/signatures/tokens_and_credentials/stripe.yaml index bde1d41..8eab7e9 100644 --- a/signatures/tokens_and_credentials/stripe.yaml +++ b/signatures/tokens_and_credentials/stripe.yaml @@ -11,6 +11,14 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.stripe.com + - STRIPE_API_KEY + - STRIPE_SECRET_KEY slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/twilio.yaml b/signatures/tokens_and_credentials/twilio.yaml index 55cbf2a..4bf7e87 100644 --- a/signatures/tokens_and_credentials/twilio.yaml +++ b/signatures/tokens_and_credentials/twilio.yaml @@ -11,6 +11,13 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.twilio.com + - TWILIO_API slack_std: category: secrets scope: diff --git a/signatures/tokens_and_credentials/twitter.yaml b/signatures/tokens_and_credentials/twitter.yaml index 7e27910..243449a 100644 --- a/signatures/tokens_and_credentials/twitter.yaml +++ b/signatures/tokens_and_credentials/twitter.yaml @@ -11,6 +11,14 @@ signatures: notes: references: watchman_apps: + stack_overflow: + scope: + - questions + - answers + search_strings: + - api.twitter.com + - twitter + - oauth_token_secret slack_std: category: secrets scope: diff --git a/tests/test_format_stack_overflow.py b/tests/test_format_stack_overflow.py new file mode 100644 index 0000000..5fb54f4 --- /dev/null +++ b/tests/test_format_stack_overflow.py @@ -0,0 +1,95 @@ +import os +import unittest +from pathlib import Path + +from models import signature_stack_overflow + +SIGNATURES_PATH = (Path(__file__).parents[1] / 'signatures').resolve() + + +def assert_empty(obj): + if obj[0] is None: + return True + else: + return False + + +def assert_not_empty(obj): + if obj[0] is None: + return False + else: + return True + + +def load_signatures_stack_overflow() -> list: + """Load signatures from YAML files + + Returns: + List containing loaded definitions as Signatures objects + """ + + loaded_signatures = [] + try: + for root, dirs, files in os.walk(SIGNATURES_PATH): + for sig_file in files: + sig_path = (Path(root) / sig_file).resolve() + if sig_path.name.endswith('.yaml'): + loaded_def = signature_stack_overflow.load_from_yaml(sig_path) + for sig in loaded_def: + if sig.status == 'enabled' and 'Stack Overflow' in sig.watchman_apps: + loaded_signatures.append(sig) + return loaded_signatures + except Exception as e: + raise e + + +class TestSigs(unittest.TestCase): + def test_signatures_format_so(self): + """Check signatures are properly formed YAML ready to be ingested for Stack Overflow Watchman""" + + try: + load_signatures_stack_overflow() + except AttributeError: + self.assertTrue(False) + + def test_search_strings_format(self): + print('Testing search string format') + signatures = load_signatures_stack_overflow() + + for sig in signatures: + if 'stack_overflow' in sig.watchman_apps: + assert isinstance(sig.search_strings, list), f'Signature {sig.name} has' \ + f' incorrectly formatted Stack Overflow search strings' + + def test_search_strings_content(self): + print('Testing search strings content') + signatures = load_signatures_stack_overflow() + + for sig in signatures: + if 'stack_overflow' in sig.watchman_apps: + self.assertTrue(assert_not_empty(sig.search_strings), f'Signature {sig.name} has no search strings.' + f' There must be at least one to be used with Stack Oveflow Watchman') + + def test_scope_format(self): + print('Testing scope options') + signatures = load_signatures_stack_overflow() + + for sig in signatures: + if 'stack_overflow' in sig.watchman_apps: + assert isinstance(sig.scope, list), f'Signature {sig.name} has' \ + f' incorrectly formatted Stack Overflow scopes' + + def test_scope_content(self): + print('Testing scope content') + signatures = load_signatures_stack_overflow() + + for sig in signatures: + if 'stack_overflow' in sig.watchman_apps: + self.assertTrue(assert_not_empty(sig.scope), f'Signature {sig.name} has no scopes.' + f' There must be at least one to be used with Stack Overflow Watchman') + +if __name__ == '__main__': + print('Running Stack Overflow signature tests') + print('-----') + unittest.main() + print('-----')