├── .github └── workflows │ ├── codeql-analysis.yml │ └── python-ci-tests.yml ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CHANGELOG.rst ├── CONTRIBUTING.md ├── LICENSE ├── MANIFEST.in ├── README.rst ├── dev-requirements.txt ├── docs ├── Makefile ├── api │ ├── modules.rst │ ├── stix2patterns.grammars.rst │ ├── stix2patterns.rst │ └── stix2patterns.test.rst ├── changelog.rst ├── conf.py ├── developers.rst ├── index.rst ├── installation.rst └── usage.rst ├── setup.cfg ├── setup.py ├── stix2patterns ├── __init__.py ├── exceptions.py ├── grammars │ ├── STIXPatternLexer.py │ ├── STIXPatternListener.py │ ├── STIXPatternParser.py │ ├── STIXPatternVisitor.py │ └── __init__.py ├── helpers.py ├── inspector.py ├── pattern.py ├── test │ ├── __init__.py │ ├── test_helpers.py │ ├── v20 │ │ ├── __init__.py │ │ ├── spec_examples.txt │ │ ├── test_inspector.py │ │ └── test_validator.py │ └── v21 │ │ ├── __init__.py │ │ ├── spec_examples.txt │ │ ├── test_inspector.py │ │ └── test_validator.py ├── v20 │ ├── __init__.py │ ├── grammars │ │ ├── STIXPatternLexer.py │ │ ├── STIXPatternListener.py │ │ ├── STIXPatternParser.py │ │ ├── STIXPatternVisitor.py │ │ └── __init__.py │ ├── inspector.py │ ├── object_validator.py │ ├── pattern.py │ └── validator.py ├── v21 │ ├── __init__.py │ ├── grammars │ │ ├── STIXPatternLexer.py │ │ ├── STIXPatternListener.py │ │ ├── STIXPatternParser.py │ │ ├── STIXPatternVisitor.py │ │ └── __init__.py │ ├── inspector.py │ ├── object_validator.py │ ├── pattern.py │ └── validator.py └── validator.py └── tox.ini /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '28 12 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /.github/workflows/python-ci-tests.yml: -------------------------------------------------------------------------------- 1 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 2 | 3 | name: cti-pattern-validator test harness 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | build: 8 | 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | python-version: [3.8, 3.9, '3.10', '3.11', '3.12'] 13 | 14 | name: Python ${{ matrix.python-version }} Build 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | - name: Install and update essential dependencies 22 | run: | 23 | pip install -U pip setuptools 24 | pip install tox-gh-actions 25 | pip install codecov 26 | - name: Test with Tox 27 | run: | 28 | tox 29 | - name: Upload coverage information to Codecov 30 | uses: codecov/codecov-action@v3 31 | with: 32 | fail_ci_if_error: true # optional (default = false) 33 | verbose: true # optional (default = false) 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # Automatically-built grammar tokens 107 | *.tokens 108 | *.interp 109 | 110 | # PyCharm 111 | .idea/ 112 | 113 | # Vim 114 | *.swp 115 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | # Skip the files in stix2patterns/grammars/ 3 | skip_glob = **/STIXPattern*.py 4 | known_third_party = 5 | antlr4, 6 | pytest, 7 | six, 8 | typing, 9 | known_first_party = stix2patterns 10 | force_sort_within_sections = 1 11 | line_length = 160 12 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v3.4.0 4 | hooks: 5 | - id: trailing-whitespace 6 | exclude: grammars 7 | - id: check-merge-conflict 8 | - repo: https://github.com/PyCQA/flake8 9 | rev: 5.0.4 10 | hooks: 11 | - id: flake8 12 | name: Check project styling 13 | exclude: grammars 14 | args: 15 | - --ignore=F403,F405 16 | - --max-line-length=160 17 | - repo: https://github.com/PyCQA/isort 18 | rev: 5.13.2 19 | hooks: 20 | - id: isort 21 | name: Sort python imports (shows diff) 22 | exclude: grammars 23 | args: ["-c", "--diff"] 24 | - id: isort 25 | name: Sort python imports (fixes files) 26 | exclude: grammars 27 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | # Required 5 | version: 2 6 | 7 | # Set the OS, Python version and other tools you might need 8 | build: 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.12" 12 | 13 | # Build documentation in the "docs/" directory with Sphinx 14 | sphinx: 15 | configuration: docs/conf.py 16 | 17 | # Build all formats (incl. pdf, epub) 18 | formats: all 19 | 20 | # Declare the Python requirements required to build your documentation 21 | # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 22 | python: 23 | install: 24 | - method: pip 25 | path: . 26 | extra_requirements: 27 | - docs 28 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | 5 | 2.0.0 - Released 2022-03-31 6 | --------------------------- 7 | 8 | * #85 Update to ANTLR 4.9 (@chisholm) 9 | * #88 Default to STIX 2.1 version patterns 10 | 11 | 1.3.2 - Released 2020-12-10 12 | --------------------------- 13 | 14 | * #79 Fix bug to prevent crashing on '*' selector in hashes (@chisholm) 15 | * #81 Fix bug with bracket checking to allow for nested parentheses (@chisholm) 16 | 17 | 1.3.1 - Released 2020-07-09 18 | --------------------------- 19 | 20 | * #75 Fix bug with SSDEEP hashes in STIX 2.1 (@emmanvg) 21 | 22 | 1.3.0 - Released 2020-03-04 23 | --------------------------- 24 | 25 | * #68 Update to ANTLR 4.8 (@chisholm) 26 | * #69 Add "visit()" methods to Pattern classes (@chisholm) 27 | * #71 Fix bug with multiple qualifiers in a pattern (@chisholm) 28 | 29 | 1.2.1 - Released 2019-11-26 30 | --------------------------- 31 | 32 | * Fix some imports for backwards compatibility 33 | 34 | 1.2.0 - Released 2019-11-22 35 | --------------------------- 36 | 37 | * #59 Fixed bug where malformed hashes would pass (@JohannKT) 38 | * #63, #64 Fixed bugs with leading and trailing whitespace (@squioc) 39 | * Support STIX 2.1 patterns 40 | * Add testing for Python 3.8 41 | 42 | 1.1.0 - Released 2018-11-20 43 | --------------------------- 44 | 45 | * Add a visitor to the ANTLR parser 46 | * Add testing for Python 3.7 47 | 48 | 1.0.0 - Released 2018-07-18 49 | --------------------------- 50 | 51 | * #34 - Add documentation on ReadTheDocs: https://stix2-patterns.readthedocs.io/ 52 | * #39 - Raise error for unexepected unused character values. 53 | * #41 - Raise error for negative REPEAT values. 54 | * #42 - Improved Timestamp validation. 55 | * #43 - Validate Base64 binary literals. 56 | * #48 - Make pattern qualifier and operator keywords case-sensitive. 57 | * Drop support for Python 2.6 and 3.3. 58 | 59 | 0.6.0 - Released 2017-11-13 60 | --------------------------- 61 | 62 | * #32 - Added a public walk() method to the Pattern class. (@chisholm) 63 | * Make repository structure match other projects. (@emmanvg) 64 | 65 | 0.5.0 - Released 2017-07-12 66 | --------------------------- 67 | 68 | * Separate object and path components in inspector. 69 | * Support "NOT" qualifier on all comparison operators. 70 | 71 | 0.4.1 - Released 2017-05-19 72 | --------------------------- 73 | 74 | * Repackaged to not use a Wheel distribution 75 | 76 | 0.4.0 - Released 2017-05-19 77 | --------------------------- 78 | 79 | * Encapsulated parsed patterns in a new Pattern class 80 | 81 | 0.3.0 - Released 2017-05-04 82 | --------------------------- 83 | 84 | * Update for STIX 2.0 WD02. 85 | * Add "inspector" module to extract features from patterns. 86 | * Improve error messages. 87 | * Update to ANTLR 4.7 88 | * Add testing for Python 2.6 and 3.6 89 | 90 | 0.2.2 - Released 2017-03-01 91 | --------------------------- 92 | 93 | * Update packaging to install correct ANTLR4 runtime depending on Python 94 | version. 95 | 96 | 0.2.0 - Released 2017-02-24 97 | --------------------------- 98 | 99 | * Initial public version. 100 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Public Participation Invited 4 | 5 | This [OASIS TC Open Repository](https://www.oasis-open.org/resources/open-repositories) ( **[github.com/oasis-open/cti-pattern-validator](https://github.com/oasis-open/cti-pattern-validator)** ) is a community public repository that supports participation by anyone, whether affiliated with OASIS or not. Substantive contributions (repository "code") and related feedback is invited from all parties, following the common conventions for participation in GitHub public repository projects. Participation is expected to be consistent with the [OASIS TC Open Repository Guidelines and Procedures](https://www.oasis-open.org/policies-guidelines/open-repositories), the [LICENSE](https://www.oasis-open.org/sites/www.oasis-open.org/files/BSD-3-Clause.txt) designated for this particular repository (BSD-3-Clause License), and the requirement for an [Individual Contributor License Agreement](https://cla-assistant.io/oasis-open/Open-Repo-admin). Please see the repository [README](https://github.com/oasis-open/cti-pattern-validator/blob/master/README.md) document for other details. 6 | 7 | ## Governance Distinct from OASIS TC Process 8 | 9 | Content accepted as "contributions" to this TC Open Repository, as [defined](#openRepoContribution) below, are distinct from any [Contributions](https://www.oasis-open.org/policies-guidelines/ipr#contributions) made to the associated [OASIS Cyber Threat Intelligence (CTI) TC](https://www.oasis-open.org/committees/cti/) itself. Participation in the associated Technical Committee is governed by the [OASIS Bylaws](https://www.oasis-open.org/policies-guidelines/bylaws), [OASIS TC Process](https://www.oasis-open.org/policies-guidelines/tc-process), [IPR Policy](https://www.oasis-open.org/policies-guidelines/ipr), and related [policies](https://www.oasis-open.org/policies-guidelines/). This TC Open Repository is not subject to the OASIS TC-related policies. TC Open Repository governance is defined by separate [participation and contribution guidelines](https://www.oasis-open.org/policies-guidelines/open-repositories) as referenced in the [OASIS TC Open Repositories Overview](https://www.oasis-open.org/resources/open-repositories/). 10 | 11 | ## Licensing Distinct from OASIS IPR Policy 12 | 13 | Because different licenses apply to the OASIS TC's specification work, and this TC Open Repository, there is no guarantee that the licensure of specific repository material will be compatible with licensing requirements of an implementation of a TC's specification. Please refer to the [LICENSE file](https://github.com/oasis-open/cti-pattern-validator/blob/master/LICENSE) for the terms of this material, and to the OASIS IPR Policy for [the terms applicable to the TC's specifications](https://www.oasis-open.org/policies-guidelines/ipr#Non-Assertion-Mode), including any applicable [declarations](https://www.oasis-open.org/committees/cti/ipr.php). 14 | 15 | ## Contributions Subject to Individual CLA 16 | 17 | Formally, "contribution" to this TC Open Repository refers to content merged into the "Code" repository (repository changes represented by code [commits](https://github.com/oasis-open/cti-pattern-validator/commits/master)), following the GitHub definition of _[contributor](https://help.github.com/articles/github-glossary/#contributor)_: "someone who has contributed to a project by having a pull request merged but does not have collaborator [*i.e.*, direct write] access." Anyone who signs the TC Open Repository [Individual Contributor License Agreement (CLA)](https://cla-assistant.io/oasis-open/Open-Repo-admin), signifying agreement with the licensing requirement, may contribute substantive content — subject to evaluation of a GitHub pull request. The main web page for this repository, as with any GitHub public repository, displays a link to a document listing contributions to the repository's default branch (filtered by Commits, Additions, and Deletions). 18 | 19 | This TC Open Repository, as with GitHub public repositories generally, also accepts public feedback from any GitHub user. Public feedback includes opening issues, authoring and editing comments, participating in conversations, making wiki edits, creating repository stars, and making suggestions via pull requests. Such feedback does not constitute an OASIS TC Open Repository [contribution](#openRepoContribution). Some details are presented under "Read permissions" in the table of [permission levels](https://help.github.com/articles/repository-permission-levels-for-an-organization/) for a GitHub organization. Technical content intended as a substantive contribution (repository "Code") to an TC Open Repository is subject to evaluation, and requires a signed Individual CLA. 20 | 21 | ## Fork-and-Pull Collaboration Model 22 | 23 | OASIS TC Open Repositories use the familiar [fork-and-pull](https://help.github.com/articles/using-pull-requests/#fork--pull) collaboration model supported by GitHub and other distributed version-control systems. Any GitHub user wishing to contribute should [fork](https://help.github.com/articles/github-glossary/#fork) the repository, make additions or other modifications, and then submit a pull request. GitHub pull requests should be accompanied by supporting [comments](https://help.github.com/articles/commenting-on-the-diff-of-a-pull-request/) and/or [issues](https://help.github.com/articles/about-issues/). Community conversations about pull requests, supported by GitHub [notifications](https://help.github.com/articles/about-notifications/), will provide the basis for a consensus determination to merge, modify, close, or take other action, as communicated by the repository [Maintainers](https://www.oasis-open.org/resources/open-repositories/maintainers-guide). 24 | 25 | ## Feedback 26 | 27 | Questions or comments about this TC Open Repository's activities should be composed as GitHub issues or comments. If use of an issue/comment is not possible or appropriate, questions may be directed by email to the [repository Maintainer(s)](https://github.com/oasis-open/cti-pattern-validator/blob/master/README.md#maintainers). Please send general questions about TC Open Repository participation to OASIS Staff at [repository-admin@oasis-open.org](mailto:repository-admin@oasis-open.org) and any specific CLA-related questions to [repository-cla@oasis-open.org](mailto:repository-cla@oasis-open.org). 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) [2016], OASIS Open 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include .isort.cfg 2 | include .*.yaml 3 | include CHANGELOG.rst 4 | include CONTRIBUTING.md 5 | include dev-requirements.txt 6 | include LICENSE 7 | include tox.ini 8 | include stix2patterns/test/v20/spec_examples.txt 9 | include stix2patterns/test/v21/spec_examples.txt 10 | 11 | recursive-include docs * 12 | prune docs/_build 13 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | cti-pattern-validator 2 | ===================== 3 | 4 | This is an `OASIS TC Open Repository 5 | `__. See the 6 | `Governance <#governance>`__ section for more information. 7 | 8 | The STIX 2 Pattern Validator is a software tool for checking the syntax of the 9 | Cyber Threat Intelligence (CTI) STIX Pattern expressions, which are used within 10 | STIX to express conditions (prepresented with the Cyber Observable data model) 11 | that indicate particular cyber threat activity. The repository contains source 12 | code, an ANTLR grammar, automated tests and associated documentation for the 13 | tool. The validator can be used as a command-line tool or as a Python library 14 | which can be included in other applications. 15 | 16 | |Build_Status| |Coverage| |Version| |Scorecard| 17 | 18 | Quickstart 19 | ---------- 20 | 21 | 1. Install with pip: 22 | 23 | .. code-block:: bash 24 | 25 | $ pip install stix2-patterns 26 | 27 | 2. Validate a pattern from the command line: 28 | 29 | .. code-block:: bash 30 | 31 | $ validate-patterns 32 | Enter a pattern to validate: [file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4'] 33 | PASS: [file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4'] 34 | 35 | 3. Validate a pattern from Python code: 36 | 37 | .. code:: python 38 | 39 | from stix2patterns.validator import run_validator 40 | 41 | pattern = "[file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4']" 42 | errors = run_validator(pattern) 43 | 44 | For more information, see the `stix2-patterns documentation `_ 45 | 46 | Governance 47 | ---------- 48 | 49 | This GitHub public repository 50 | (**https://github.com/oasis-open/cti-pattern-validator** ) was `proposed 51 | `__ and 52 | `approved `__ [`bis 53 | `__\ ] by the `OASIS Cyber 54 | Threat Intelligence (CTI) TC `__ as 55 | an `OASIS TC Open Repository 56 | `__ to support 57 | development of open source resources related to Technical Committee work. 58 | 59 | While this TC Open Repository remains associated with the sponsor TC, its 60 | development priorities, leadership, intellectual property terms, participation 61 | rules, and other matters of governance are `separate and distinct 62 | `__ 63 | from the OASIS TC Process and related policies. 64 | 65 | All contributions made to this TC Open Repository are subject to open source 66 | license terms expressed in the `BSD-3-Clause License 67 | `__. 68 | That license was selected as the declared `"Applicable License" 69 | `__ when the TC 70 | Open Repository was created. 71 | 72 | As documented in `"Public Participation Invited 73 | `__", 74 | contributions to this OASIS TC Open Repository are invited from all parties, 75 | whether affiliated with OASIS or not. Participants must have a GitHub account, 76 | but no fees or OASIS membership obligations are required. Participation is 77 | expected to be consistent with the `OASIS TC Open Repository Guidelines and 78 | Procedures 79 | `__, 80 | the open source `LICENSE 81 | `__ 82 | designated for this particular repository, and the requirement for an 83 | `Individual Contributor License Agreement 84 | `__ 85 | that governs intellectual property. 86 | 87 | Maintainers 88 | ~~~~~~~~~~~ 89 | 90 | TC Open Repository `Maintainers 91 | `__ 92 | are responsible for oversight of this project's community development 93 | activities, including evaluation of GitHub `pull requests 94 | `__ 95 | and `preserving 96 | `__ 97 | open source principles of openness and fairness. Maintainers are recognized and 98 | trusted experts who serve to implement community goals and consensus design 99 | preferences. 100 | 101 | Initially, the associated TC members have designated one or more persons to 102 | serve as Maintainer(s); subsequently, participating community members may select 103 | additional or substitute Maintainers, per `consensus agreements 104 | `__. 105 | 106 | .. _currentMaintainers: 107 | 108 | Current Maintainers of this TC Open Repository 109 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 110 | 111 | - `Jason Keirstead `__; GitHub ID: 112 | https://github.com/JasonKeirstead; WWW: `IBM `__ 113 | - `Emily Ratliff `__; GitHub ID: 114 | https://github.com/ejratl; WWW: `IBM `__ 115 | - `Duncan Sparrell `__; GitHub ID: 116 | https://github.com/sparrell; WWW: `sFractal `__ 117 | 118 | About OASIS TC Open Repositories 119 | -------------------------------- 120 | 121 | - `TC Open Repositories: Overview and Resources 122 | `_ 123 | - `Frequently Asked Questions 124 | `_ 125 | - `Open Source Licenses 126 | `_ 127 | - `Contributor License Agreements (CLAs) 128 | `_ 129 | - `Maintainers' Guidelines and Agreement 130 | `_ 131 | 132 | Feedback 133 | -------- 134 | 135 | Questions or comments about this TC Open Repository's activities should be 136 | composed as GitHub issues or comments. If use of an issue/comment is not 137 | possible or appropriate, questions may be directed by email to the Maintainer(s) 138 | `listed above <#currentmaintainers>`__. Please send general questions about TC 139 | Open Repository participation to OASIS Staff at repository-admin@oasis-open.org 140 | and any specific CLA-related questions to repository-cla@oasis-open.org. 141 | 142 | .. |Build_Status| image:: https://github.com/oasis-open/cti-pattern-validator/workflows/cti-pattern-validator%20test%20harness/badge.svg 143 | :target: https://github.com/oasis-open/cti-pattern-validator/actions?query=workflow%3A%22cti-pattern-validator+test+harness%22 144 | .. |Coverage| image:: https://codecov.io/gh/oasis-open/cti-pattern-validator/branch/master/graph/badge.svg 145 | :target: https://codecov.io/gh/oasis-open/cti-pattern-validator 146 | .. |Version| image:: https://img.shields.io/pypi/v/stix2-patterns.svg?maxAge=3600 147 | :target: https://pypi.org/project/stix2-patterns/ 148 | .. |Scorecard| image:: https://api.securityscorecards.dev/projects/github.com/oasis-open/cti-pattern-validator/badge 149 | :target: https://api.securityscorecards.dev/projects/github.com/oasis-open/cti-pattern-validator 150 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | -e .[dev] 2 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = stix2-patterns 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/api/modules.rst: -------------------------------------------------------------------------------- 1 | stix2patterns 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | stix2patterns 8 | -------------------------------------------------------------------------------- /docs/api/stix2patterns.grammars.rst: -------------------------------------------------------------------------------- 1 | stix2patterns.grammars package 2 | ============================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | stix2patterns.grammars.STIXPatternLexer module 8 | ---------------------------------------------- 9 | 10 | .. automodule:: stix2patterns.grammars.STIXPatternLexer 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | stix2patterns.grammars.STIXPatternListener module 16 | ------------------------------------------------- 17 | 18 | .. automodule:: stix2patterns.grammars.STIXPatternListener 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | stix2patterns.grammars.STIXPatternParser module 24 | ----------------------------------------------- 25 | 26 | .. automodule:: stix2patterns.grammars.STIXPatternParser 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: stix2patterns.grammars 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /docs/api/stix2patterns.rst: -------------------------------------------------------------------------------- 1 | stix2patterns package 2 | ===================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | stix2patterns.grammars 10 | stix2patterns.test 11 | 12 | Submodules 13 | ---------- 14 | 15 | stix2patterns.inspector module 16 | ------------------------------ 17 | 18 | .. automodule:: stix2patterns.inspector 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | stix2patterns.pattern module 24 | ---------------------------- 25 | 26 | .. automodule:: stix2patterns.pattern 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | stix2patterns.validator module 32 | ------------------------------ 33 | 34 | .. automodule:: stix2patterns.validator 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | 40 | Module contents 41 | --------------- 42 | 43 | .. automodule:: stix2patterns 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | -------------------------------------------------------------------------------- /docs/api/stix2patterns.test.rst: -------------------------------------------------------------------------------- 1 | stix2patterns.test package 2 | ========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | stix2patterns.test.test\_inspector module 8 | ----------------------------------------- 9 | 10 | .. automodule:: stix2patterns.test.test_inspector 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | stix2patterns.test.test\_validator module 16 | ----------------------------------------- 17 | 18 | .. automodule:: stix2patterns.test.test_validator 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: stix2patterns.test 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGELOG.rst -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | project = 'stix2-patterns' 4 | copyright = '2018, OASIS Open' 5 | author = 'OASIS Open' 6 | 7 | version = '2.0.0' 8 | release = '2.0.0' 9 | 10 | extensions = [ 11 | 'sphinx.ext.autodoc', 12 | 'sphinx.ext.napoleon', 13 | 'sphinx.ext.viewcode', 14 | 'sphinx-prompt', 15 | ] 16 | 17 | templates_path = ['_templates'] 18 | 19 | source_suffix = '.rst' 20 | 21 | master_doc = 'index' 22 | 23 | language = None 24 | 25 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 26 | 27 | pygments_style = 'sphinx' 28 | 29 | 30 | html_theme = 'alabaster' 31 | 32 | html_static_path = ['_static'] 33 | 34 | htmlhelp_basename = 'stix2-patternsdoc' 35 | 36 | 37 | latex_elements = {} 38 | 39 | latex_documents = [ 40 | (master_doc, 'stix2-patterns.tex', 'stix2-patterns Documentation', 41 | 'OASIS Open', 'manual'), 42 | ] 43 | 44 | man_pages = [ 45 | (master_doc, 'stix2-patterns', 'stix2-patterns Documentation', 46 | [author], 1) 47 | ] 48 | 49 | 50 | texinfo_documents = [ 51 | (master_doc, 'stix2-patterns', 'stix2-patterns Documentation', 52 | author, 'stix2-patterns', 'One line description of project.', 53 | 'Miscellaneous'), 54 | ] 55 | -------------------------------------------------------------------------------- /docs/developers.rst: -------------------------------------------------------------------------------- 1 | Developer's Guide 2 | ================= 3 | 4 | We're thrilled that you're interested in contributing to stix2-patterns! Here 5 | are some things you should know: 6 | 7 | - `contribution-guide.org `_ has great ideas 8 | for contributing to any open-source project (not just this one). 9 | - All contributors must sign a Contributor License Agreement. See 10 | `CONTRIBUTING.md `_ 11 | in the project repository for specifics. 12 | - If you are planning to implement a major feature (vs. fixing a bug), please 13 | discuss with a project maintainer first to ensure you aren't duplicating the 14 | work of someone else, and that the feature is likely to be accepted. 15 | 16 | Now, let's get started! 17 | 18 | Setting up a development environment 19 | ------------------------------------ 20 | 21 | We recommend using a `virtualenv `_. 22 | 23 | 1. Clone the repository. If you're planning to make pull request, you should fork 24 | the repository on GitHub and clone your fork instead of the main repo: 25 | 26 | .. prompt:: bash 27 | 28 | git clone https://github.com/yourusername/cti-pattern-validator.git 29 | 30 | 2. Install develoment-related dependencies: 31 | 32 | .. prompt:: bash 33 | 34 | cd cti-pattern-validator 35 | pip install -r requirements.txt 36 | 37 | 3. Install `pre-commit `_ git hooks: 38 | 39 | .. prompt:: bash 40 | 41 | pre-commit install 42 | 43 | At this point you should be able to make changes to the code. 44 | 45 | Code style 46 | ---------- 47 | 48 | All code should follow `PEP 8 `_. We 49 | allow for line lengths up to 160 characters, but any lines over 80 characters 50 | should be the exception rather than the rule. PEP 8 conformance will be tested 51 | automatically by Tox and Travis-CI (see below). 52 | 53 | Updating the Grammar 54 | -------------------- 55 | 56 | The ANTLR pattern grammar is maintained in the `stix2-json-schemas 57 | `__ 58 | repository. If the grammar changes, the code in this repository should be 59 | updated to match. To do so, use the Java ANTLR package to generate new Python 60 | source files. (The .jar file is not needed for normal use of the validator). 61 | 62 | 1. Download antlr-4.7.1-complete.jar from http://www.antlr.org/download/ 63 | 2. Clone the stix2-json-schemas repository or download the STIXPattern.g4 file. 64 | 3. Change to the directory containing the STIXPattern.g4 file. 65 | 4. Run the following command (for STIX v2.1) 66 | 67 | .. prompt:: bash 68 | 69 | java -jar "/path/to/antlr-4.7.1-complete.jar" -Dlanguage=Python2 STIXPattern.g4 -visitor -o /path/to/cti-pattern-validator/stix2patterns/v21/grammars 70 | 71 | 5. Commit the resulting files to git. 72 | 73 | Testing 74 | ------- 75 | 76 | .. note:: 77 | 78 | All of the tools mentioned in this section are installed when you run ``pip 79 | install -r requirements.txt``. 80 | 81 | This project uses `pytest `_ for testing. We encourage the 82 | use of test-driven development (TDD), where you write (failing) tests that 83 | demonstrate a bug or proposed new feature before writing code that fixes the bug 84 | or implements the features. Any code contributions should come with new or 85 | updated tests. 86 | 87 | To run the tests in your current Python environment, use the ``pytest`` command 88 | from the root project directory: 89 | 90 | .. prompt:: bash 91 | 92 | pytest 93 | 94 | This should show all of the tests that ran, along with their status. 95 | 96 | You can run a specific test file by passing it on the command line: 97 | 98 | .. prompt:: bash 99 | 100 | pytest stix2patterns/test/v21/test_.py 101 | 102 | You can also test against the examples provided in the supplied example file. 103 | Note that you must specify which version to test. 104 | 105 | .. prompt:: bash 106 | 107 | validate-patterns -v 2.1 -f stix2patterns/test/v21/spec_examples.txt 108 | 109 | To ensure that the test you wrote is running, you can deliberately add an 110 | ``assert False`` statement at the beginning of the test. This is another benefit 111 | of TDD, since you should be able to see the test failing (and ensure it's being 112 | run) before making it pass. 113 | 114 | `tox `_ allows you to test a package 115 | across multiple versions of Python. Setting up multiple Python environments is 116 | beyond the scope of this guide, but feel free to ask for help setting them up. 117 | Tox should be run from the root directory of the project: 118 | 119 | .. prompt:: bash 120 | 121 | tox 122 | 123 | We aim for high test coverage, using the `coverage.py 124 | `_ library. Though it's not an 125 | absolute requirement to maintain 100% coverage, all code contributions must 126 | be accompanied by tests. To run coverage and look for untested lines of code, 127 | run: 128 | 129 | .. prompt:: bash 130 | 131 | pytest --cov=stix2patterns 132 | coverage html 133 | 134 | then look at the resulting report in ``htmlcov/index.html``. 135 | 136 | All commits pushed to the ``master`` branch or submitted as a pull request are 137 | tested with `Travis-CI `_ 138 | automatically. 139 | 140 | Adding a dependency 141 | ------------------- 142 | 143 | One of the pre-commit hooks we use in our develoment environment enforces a 144 | consistent ordering to imports. If you need to add a new library as a dependency 145 | please add it to the `known_third_party` section of `.isort.cfg` to make sure 146 | the import is sorted correctly. 147 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to stix2-patterns's documentation! 2 | ========================================== 3 | 4 | The STIX 2 Pattern Validator is a software tool for checking the syntax of the 5 | Cyber Threat Intelligence (CTI) STIX Pattern expressions, which are used within 6 | STIX to express conditions (prepresented with the Cyber Observable data model) 7 | that indicate particular cyber threat activity. The repository_ contains source 8 | code, an ANTLR grammar, and automated tests for the 9 | tool. The validator can be used as a command-line tool or as a Python library 10 | which can be included in other applications. 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | :caption: Contents: 15 | 16 | installation 17 | usage 18 | developers 19 | changelog 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | :caption: API Documentation: 24 | 25 | api/modules 26 | 27 | Indices and tables 28 | ================== 29 | 30 | * :ref:`genindex` 31 | * :ref:`modindex` 32 | * :ref:`search` 33 | 34 | .. _repository: https://github.com/oasis-open/cti-pattern-validator 35 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Requirements 5 | ------------ 6 | 7 | - `Python `__ 2.7 or 3.4+ 8 | - ANTLR grammar runtime (4.7 or newer): 9 | 10 | - `antlr4-python2-runtime `__ 11 | (Python 2.7) 12 | - `antlr4-python3-runtime `__ 13 | (Python 3) 14 | 15 | - `six `__ 16 | - `typing `__ (Python 3.4) 17 | 18 | Install Package 19 | --------------- 20 | 21 | Using `pip `__ is highly recommended: 22 | 23 | .. code:: bash 24 | 25 | $ pip install stix2-patterns 26 | 27 | For more information about installing Python packages, see the `Python 28 | Packaging User Guide 29 | `__. 30 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | The STIX Pattern Validator provides an executable script (``validate-patterns``) 5 | in addition to being an importable Python library. 6 | 7 | The ``validate-patterns`` script accepts patterns from either direct user input 8 | or a file passed as an option. 9 | 10 | From Python Code 11 | ---------------- 12 | 13 | The ``run_validator`` function can be called on any Python string. It returns a 14 | list of errors encountered while parsing the pattern. 15 | 16 | .. code:: python 17 | 18 | from stix2patterns.validator import run_validator 19 | 20 | pattern = "[file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4']" 21 | errors = run_validator(pattern) 22 | 23 | User Input 24 | ---------- 25 | 26 | When prompted, enter a pattern to validate and press enter. The validator will 27 | supply whether the pattern has passed or failed. If the pattern fails the test, 28 | the validator will supply where the first syntax error occurred. The validator 29 | will continue to prompt for patterns until Ctrl-C is pressed. Example: 30 | 31 | .. code:: bash 32 | 33 | $ validate-patterns 34 | 35 | Enter a pattern to validate: [file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4'] 36 | 37 | PASS: [file-object:hashes.md5 = '79054025255fb1a26e4bc422aef54eb4'] 38 | 39 | File Input 40 | ---------- 41 | 42 | .. code:: bash 43 | 44 | $ validate-patterns -f 45 | 46 | Use to specify the path to a file containing a set of patterns 47 | to validate. Each pattern must be on a separate line of the file so that the 48 | validator may determine where the pattern begins and ends. The validator will 49 | supply the PASS/FAIL result of each pattern. 50 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 2.0.0 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:docs/conf.py] 7 | 8 | [bumpversion:file:setup.py] 9 | 10 | [metadata] 11 | license_file = LICENSE 12 | 13 | [bdist_wheel] 14 | universal = 1 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import find_packages, setup 4 | 5 | with open('README.rst') as f: 6 | readme = f.read() 7 | 8 | doc_requires = [ 9 | 'sphinx', 10 | 'sphinx-prompt', 11 | ] 12 | 13 | test_requires = [ 14 | 'coverage', 15 | 'pytest', 16 | 'pytest-cov', 17 | ] 18 | 19 | dev_requires = doc_requires + test_requires + [ 20 | 'bumpversion', 21 | 'check-manifest', 22 | 'pre-commit', 23 | # test_requires are installed into every tox environment, so we don't 24 | # want to include tox there. 25 | 'tox', 26 | ] 27 | 28 | setup( 29 | name='stix2-patterns', 30 | version='2.0.0', 31 | description='Validate STIX 2 Patterns.', 32 | long_description=readme, 33 | long_description_content_type='text/x-rst', 34 | url="https://github.com/oasis-open/cti-pattern-validator", 35 | author='OASIS Cyber Threat Intelligence Technical Committee', 36 | author_email='cti-users@lists.oasis-open.org', 37 | python_requires=">=3.8", 38 | packages=find_packages(), 39 | install_requires=[ 40 | 'antlr4-python3-runtime~=4.9.0', 41 | 'six', 42 | ], 43 | package_data={ 44 | 'stix2patterns.test.v20': ['spec_examples.txt'], 45 | 'stix2patterns.test.v21': ['spec_examples.txt'], 46 | }, 47 | entry_points={ 48 | 'console_scripts': [ 49 | 'validate-patterns = stix2patterns.validator:main', 50 | ], 51 | }, 52 | classifiers=[ 53 | 'Development Status :: 3 - Alpha', 54 | 'License :: OSI Approved :: BSD License', 55 | 'Programming Language :: Python :: 3', 56 | 'Programming Language :: Python :: 3.8', 57 | 'Programming Language :: Python :: 3.9', 58 | 'Programming Language :: Python :: 3.10', 59 | 'Programming Language :: Python :: 3.11', 60 | 'Programming Language :: Python :: 3.12', 61 | ], 62 | extras_require={ 63 | 'dev': dev_requires, 64 | 'docs': doc_requires, 65 | 'test': test_requires, 66 | }, 67 | ) 68 | -------------------------------------------------------------------------------- /stix2patterns/__init__.py: -------------------------------------------------------------------------------- 1 | DEFAULT_VERSION = '2.1' # Default version should always be the latest STIX 2.X version 2 | -------------------------------------------------------------------------------- /stix2patterns/exceptions.py: -------------------------------------------------------------------------------- 1 | from antlr4.error.ErrorListener import ErrorListener 2 | 3 | 4 | class STIXPatternErrorListener(ErrorListener): 5 | """ 6 | Modifies ErrorListener to collect error message and set flag to False when 7 | invalid pattern is encountered. 8 | """ 9 | def __init__(self): 10 | super(STIXPatternErrorListener, self).__init__() 11 | self.err_strings = [] 12 | 13 | def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): 14 | self.err_strings.append("FAIL: Error found at line %d:%d. %s" % 15 | (line, column, msg)) 16 | 17 | 18 | class ParserErrorListener(ErrorListener): 19 | """ 20 | Simple error listener which just remembers the last error message received. 21 | """ 22 | def syntaxError(self, recognizer, offendingSymbol, line, column, msg, e): 23 | self.error_message = u"{}:{}: {}".format(line, column, msg) 24 | 25 | 26 | class ParseException(Exception): 27 | """Represents a parse error.""" 28 | pass 29 | -------------------------------------------------------------------------------- /stix2patterns/grammars/STIXPatternLexer.py: -------------------------------------------------------------------------------- 1 | # Update or remove for 2.0.0 2 | from ..v20.grammars.STIXPatternLexer import * # noqa: F401 -------------------------------------------------------------------------------- /stix2patterns/grammars/STIXPatternListener.py: -------------------------------------------------------------------------------- 1 | # Update or remove for 2.0.0 2 | from ..v20.grammars.STIXPatternListener import * # noqa: F401 -------------------------------------------------------------------------------- /stix2patterns/grammars/STIXPatternParser.py: -------------------------------------------------------------------------------- 1 | # Update or remove for 2.0.0 2 | from ..v20.grammars.STIXPatternParser import * # noqa: F401 -------------------------------------------------------------------------------- /stix2patterns/grammars/STIXPatternVisitor.py: -------------------------------------------------------------------------------- 1 | # Update or remove for 2.0.0 2 | from ..v20.grammars.STIXPatternVisitor import * # noqa: F401 -------------------------------------------------------------------------------- /stix2patterns/grammars/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/grammars/__init__.py -------------------------------------------------------------------------------- /stix2patterns/helpers.py: -------------------------------------------------------------------------------- 1 | import six 2 | 3 | 4 | def brackets_check(pattern): 5 | """ 6 | Check whether the pattern is missing square brackets, in a way which does 7 | not require the usual parsing. This is a light hack to provide an improved 8 | error message in this particular case. 9 | 10 | :param pattern: A STIX pattern string 11 | :return: True if the pattern had its brackets; False if not 12 | """ 13 | if isinstance(pattern, six.string_types): 14 | 15 | # There can be an arbitrary number of open parens first... skip over 16 | # those 17 | for c in pattern: 18 | if c != "(" and not c.isspace(): 19 | break 20 | 21 | if c == "[": 22 | result = True 23 | else: 24 | result = False 25 | 26 | else: 27 | result = False 28 | 29 | return result 30 | -------------------------------------------------------------------------------- /stix2patterns/inspector.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | 4 | class InspectionException(Exception): 5 | """Represents a error that occurred during inspection.""" 6 | pass 7 | 8 | 9 | _PatternData = collections.namedtuple("pattern_data", 10 | "comparisons observation_ops qualifiers") 11 | 12 | 13 | # For representing a "star" array index step in an object path 14 | INDEX_STAR = object() 15 | 16 | 17 | def _string_literal_to_string(string_literal_token): 18 | """Converts the StringLiteral token to a plain string: get text content, 19 | removes quote characters, and unescapes it. 20 | 21 | :param string_literal_token: The string literal 22 | :return: 23 | """ 24 | token_text = string_literal_token.getText() 25 | return token_text[1:-1].replace(u"\\'", u"'"). \ 26 | replace(u"\\\\", u"\\") 27 | -------------------------------------------------------------------------------- /stix2patterns/pattern.py: -------------------------------------------------------------------------------- 1 | # Update or remove for 2.0.0 2 | from .exceptions import ParseException, ParserErrorListener # noqa: F401 3 | from .v20.pattern import Pattern # noqa: F401 4 | -------------------------------------------------------------------------------- /stix2patterns/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/test/__init__.py -------------------------------------------------------------------------------- /stix2patterns/test/test_helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test cases for stix2patterns/helpers.py. 3 | """ 4 | import pytest 5 | 6 | from stix2patterns.helpers import brackets_check 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "value", [ 11 | '[file:size = 1280]', 12 | ' [file:size = 1280]', 13 | '( [file:size = 1280])', 14 | '( ( [file:size = 1280]) )', 15 | '(( ( ( [file:size = 1280])) ))', 16 | '[', 17 | ], 18 | ) 19 | def test_brackets_check(value): 20 | assert brackets_check(value) 21 | 22 | 23 | @pytest.mark.parametrize( 24 | "value", [ 25 | None, 26 | "file:size = 1280", 27 | "(file:size = 1280)", 28 | " ( file:size = 1280 ) ", 29 | " (( (( file:size = 1280 ) )) ) ", 30 | ] 31 | ) 32 | def test_brackets_check_fail(value): 33 | assert not brackets_check(None) 34 | -------------------------------------------------------------------------------- /stix2patterns/test/v20/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/test/v20/__init__.py -------------------------------------------------------------------------------- /stix2patterns/test/v20/spec_examples.txt: -------------------------------------------------------------------------------- 1 | [file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f'] 2 | [email-message:from_ref.value MATCHES '.+\\@example\\.com$' AND email-message:body_multipart[*].body_raw_ref.name MATCHES '^Final Report.+\\.exe$'] 3 | [file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f' AND file:mime_type = 'application/x-pdf'] 4 | [file:hashes.'SHA-256' = 'bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c' OR file:hashes.MD5 = 'cead3f77f6cda6ec00f57d76c9a6879f'] AND [file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f'] 5 | ([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\foo\\bar']) WITHIN 300 SECONDS 6 | [user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1009' AND user-account:account_login = 'Mary'] 7 | [artifact:mime_type = 'application/vnd.tcpdump.pcap' AND artifact:payload_bin MATCHES '\\xd4\\xc3\\xb2\\xa1\\x02\\x00\\x04\\x00'] 8 | [file:name = 'foo.dll' AND file:parent_directory_ref.path = 'C:\\Windows\\System32'] 9 | [file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0] 10 | [file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8'] 11 | [network-traffic:dst_ref.type = 'ipv4-addr' AND network-traffic:dst_ref.value = '203.0.113.33/32'] 12 | [network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS 13 | [domain-name:value = 'www.5z8.info' AND domain-name:resolves_to_refs[*].value = '198.51.100.1/32'] 14 | [url:value = 'http://example.com/foo' OR url:value = 'http://example.com/bar'] 15 | [x509-certificate:issuer = 'CN=WEBMAIL' AND x509-certificate:serial_number = '4c:0b:1d:19:74:86:a7:66:b4:1a:bf:40:27:21:76:28'] 16 | [windows-registry-key:key = 'HKEY_CURRENT_USER\\Software\\CryptoLocker\\Files' OR windows-registry-key:key = 'HKEY_CURRENT_USER\\Software\\Microsoft\\CurrentVersion\\Run\\CryptoLocker_0388'] 17 | [(file:name = 'pdf.exe' OR file:size = '371712') AND file:created = t'2014-01-13T07:03:17Z'] 18 | [email-message:sender_ref.value = 'jdoe@example.com' AND email-message:subject = 'Conference Info'] 19 | [x-usb-device:usbdrive.serial_number = '575833314133343231313937'] 20 | [process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachine Root$'] FOLLOWEDBY [process:command_line MATCHES'^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$'] WITHIN 300 SECONDS 21 | [network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64'] 22 | ([file:name = 'foo.dll'] AND [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\foo\\bar']) OR [process:name = 'fooproc' OR process:name = 'procfoo'] 23 | [file:hashes.MD5 = 'cead3f77f6cda6ec00f57d76c9a69faa'] 24 | ( [(network-traffic:dst_port IN(443,6443,8443) AND network-traffic:src_packets != 0) ]) 25 | -------------------------------------------------------------------------------- /stix2patterns/test/v20/test_inspector.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from stix2patterns.inspector import INDEX_STAR 4 | from stix2patterns.v20.pattern import Pattern 5 | 6 | 7 | @pytest.mark.parametrize(u"pattern,expected_qualifiers", [ 8 | (u"[foo:bar = 1]", set()), 9 | (u"[foo:bar = 1] REPEATS 5 TIMES", set([u"REPEATS 5 TIMES"])), 10 | (u"[foo:bar = 1] WITHIN 10.3 SECONDS", set([u"WITHIN 10.3 SECONDS"])), 11 | (u"[foo:bar = 1] WITHIN 123 SECONDS", set([u"WITHIN 123 SECONDS"])), 12 | (u"[foo:bar = 1] START '1932-11-12T15:42:15Z' STOP '1964-10-53T21:12:26Z'", 13 | set([u"START '1932-11-12T15:42:15Z' STOP '1964-10-53T21:12:26Z'"])), 14 | (u"[foo:bar = 1] REPEATS 1 TIMES REPEATS 2 TIMES", 15 | set([u"REPEATS 1 TIMES", u"REPEATS 2 TIMES"])), 16 | (u"[foo:bar = 1] REPEATS 1 TIMES AND [foo:baz = 2] WITHIN 1.23 SECONDS", 17 | set([u"REPEATS 1 TIMES", u"WITHIN 1.23 SECONDS"])), 18 | (u"([foo:bar = 1] START '1932-11-12T15:42:15Z' STOP '1964-10-53T21:12:26Z' AND [foo:abc < h'12ab']) WITHIN 22 SECONDS " 19 | u"OR [frob:baz NOT IN (1,2,3)] REPEATS 31 TIMES", 20 | set([u"START '1932-11-12T15:42:15Z' STOP '1964-10-53T21:12:26Z'", 21 | u"WITHIN 22 SECONDS", u"REPEATS 31 TIMES"])) 22 | ]) 23 | def test_qualifiers(pattern, expected_qualifiers): 24 | compiled_pattern = Pattern(pattern) 25 | pattern_data = compiled_pattern.inspect() 26 | 27 | assert pattern_data.qualifiers == expected_qualifiers 28 | 29 | 30 | @pytest.mark.parametrize(u"pattern,expected_obs_ops", [ 31 | (u"[foo:bar = 1]", set()), 32 | (u"[foo:bar = 1] AND [foo:baz > 25.2]", set([u"AND"])), 33 | (u"[foo:bar = 1] OR [foo:baz != 'hello']", set([u"OR"])), 34 | (u"[foo:bar = 1] FOLLOWEDBY [foo:baz IN (1,2,3)]", set([u"FOLLOWEDBY"])), 35 | (u"[foo:bar = 1] AND [foo:baz = 22] OR [foo:abc = '123']", set([u"AND", u"OR"])), 36 | (u"[foo:bar = 1] OR ([foo:baz = false] FOLLOWEDBY [frob:abc LIKE '123']) WITHIN 46.1 SECONDS", 37 | set([u"OR", u"FOLLOWEDBY"])) 38 | ]) 39 | def test_observation_ops(pattern, expected_obs_ops): 40 | compiled_pattern = Pattern(pattern) 41 | pattern_data = compiled_pattern.inspect() 42 | 43 | assert pattern_data.observation_ops == expected_obs_ops 44 | 45 | 46 | @pytest.mark.parametrize(u"pattern,expected_comparisons", [ 47 | (u"[foo:bar = 1]", {u"foo": [([u"bar"], u"=", u"1")]}), 48 | (u"[foo:bar=1 AND foo:baz=2]", {u"foo": [([u"bar"], u"=", u"1"), ([u"baz"], u"=", u"2")]}), 49 | (u"[foo:bar NOT !=1 OR bar:foo<12.3]", { 50 | u"foo": [([u"bar"], u"NOT !=", u"1")], 51 | u"bar": [([u"foo"], u"<", u"12.3")] 52 | }), 53 | (u"[foo:bar=1] OR [foo:baz MATCHES '123\\\\d+']", { 54 | u"foo": [([u"bar"], u"=", u"1"), ([u"baz"], u"MATCHES", u"'123\\\\d+'")] 55 | }), 56 | (u"[foo:bar=1 AND bar:foo NOT >33] REPEATS 12 TIMES OR " 57 | u" ([baz:bar ISSUBSET '1234'] FOLLOWEDBY [baz:quux NOT LIKE 'a_cd'])", 58 | { 59 | u"foo": [([u"bar"], u"=", u"1")], 60 | u"bar": [([u"foo"], u"NOT >", u"33")], 61 | u"baz": [([u"bar"], u"ISSUBSET", u"'1234'"), ([u"quux"], u"NOT LIKE", u"'a_cd'")] 62 | }), 63 | (u"[obj-type:a.b[*][1].'c-d' NOT ISSUPERSET '1.2.3.4/16']", { 64 | u"obj-type": [([u"a", u"b", INDEX_STAR, 1, u"c-d"], u"NOT ISSUPERSET", u"'1.2.3.4/16'")] 65 | }), 66 | ]) 67 | def test_comparisons(pattern, expected_comparisons): 68 | compiled_pattern = Pattern(pattern) 69 | pattern_data = compiled_pattern.inspect() 70 | 71 | assert pattern_data.comparisons == expected_comparisons 72 | -------------------------------------------------------------------------------- /stix2patterns/test/v20/test_validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test cases for stix2patterns/validator.py. 3 | """ 4 | import os 5 | 6 | import pytest 7 | 8 | from stix2patterns.validator import validate 9 | 10 | TEST_CASE_FILE = os.path.join(os.path.dirname(__file__), 'spec_examples.txt') 11 | with open(TEST_CASE_FILE) as f: 12 | SPEC_CASES = [x.strip() for x in f.readlines()] 13 | 14 | 15 | @pytest.mark.parametrize("test_input", SPEC_CASES) 16 | def test_spec_patterns(test_input): 17 | """ 18 | Validate patterns from STIX 2.0 Patterning spec. 19 | """ 20 | pass_test = validate(test_input, stix_version='2.0', print_errs=True) 21 | assert pass_test is True 22 | 23 | 24 | FAIL_CASES = [ 25 | ("file:size = 1280", # Does not use square brackets 26 | "FAIL: Error found at line 1:0. input is missing square brackets"), 27 | ("[file:size = ]", # Missing value 28 | "FAIL: Error found at line 1:13. mismatched input ']'"), 29 | ("[file:hashes.MD5 = cead3f77f6cda6ec00f57d76c9a6879f]", # No quotes around string 30 | "FAIL: Error found at line 1:19. mismatched input 'cead3f77f6cda6ec00f57d76c9a6879f'"), 31 | ("[file.size = 1280]", # Use period instead of colon 32 | "FAIL: Error found at line 1:5. no viable alternative at input 'file.'"), 33 | ("[file:name MATCHES /.*\\.dll/]", # Quotes around regular expression 34 | "FAIL: Error found at line 1:19. mismatched input '/' expecting StringLiteral"), 35 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN ]", # Missing Qualifier value 36 | "FAIL: Error found at line 1:63. mismatched input ']' expecting {IntPosLiteral, FloatPosLiteral}"), 37 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 HOURS]", # SECONDS is the only valid time unit 38 | "FAIL: Error found at line 1:65. mismatched input 'HOURS' expecting 'SECONDS'"), 39 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN -5 SECONDS]", # Negative integer is invalid 40 | "FAIL: Error found at line 1:63. mismatched input '-5' expecting {IntPosLiteral, FloatPosLiteral}"), 41 | ("[network-traffic:dst_ref.value ISSUBSET ]", # Missing second Comparison operand 42 | "FAIL: Error found at line 1:40. missing StringLiteral at ']'"), 43 | ("[file:hashes.MD5 =? 'cead3f77f6cda6ec00f57d76c9a6879f']", # '=?' isn't a valid operator 44 | "FAIL: Error found at line 1:18. extraneous input '?'"), 45 | ("[x_whatever:detected == t'2457-73-22T32:81:84.1Z']", # Not a valid date 46 | "FAIL: Error found at line 1:24. extraneous input 't'"), 47 | ("[artifact:payload_bin = b'====']", # Not valid Base64 48 | "FAIL: Error found at line 1:24. extraneous input 'b'"), 49 | ("[foo:bar=1] within 2 seconds", # keywords must be uppercase 50 | "FAIL: Error found at line 1:12. mismatched input 'within' expecting "), 51 | ("[file:hashes.'SHA-256' = 'f00']", # Malformed hash value 52 | "FAIL: 'f00' is not a valid SHA-256 hash"), 53 | # TODO: add more failing test cases. 54 | ] 55 | 56 | 57 | @pytest.mark.parametrize("test_input,test_output", FAIL_CASES) 58 | def test_fail_patterns(test_input, test_output): 59 | """ 60 | Validate that patterns fail as expected. 61 | """ 62 | pass_test, errors = validate(test_input, stix_version='2.0', ret_errs=True, print_errs=True) 63 | assert errors[0].startswith(test_output) 64 | assert pass_test is False 65 | 66 | 67 | PASS_CASES = [ 68 | "[file:size = 1280]", 69 | "[file:size != 1280]", 70 | "[file:size < 1024]", 71 | "[file:size <= 1024]", 72 | "[file:size > 1024]", 73 | "[file:size >= 1024]", 74 | "[file:file_name = 'my_file_name']", 75 | "[file:extended_properties.'ntfs-ext'.sid = '234']", 76 | r"[emailaddr:value MATCHES '.+\\@ibm\\.com$' OR file:name MATCHES '^Final Report.+\\.exe$']", 77 | "[ipv4addr:value ISSUBSET '192.168.0.1/24']", 78 | "[ipv4addr:value NOT ISSUBSET '192.168.0.1/24']", 79 | "[user-account:value = 'Peter'] AND [user-account:value != 'Paul'] AND [user-account:value = 'Mary'] WITHIN 5 SECONDS", 80 | "[file:file_system_properties.file_name LIKE 'name%']", 81 | "[file:file_name IN ('test.txt', 'test2.exe', 'README')]", 82 | "[file:size IN (1024, 2048, 4096)]", 83 | "[network-connection:extended_properties[0].source_payload MATCHES 'dGVzdHRlc3R0ZXN0']", 84 | "[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 SECONDS", 85 | "[x_whatever:detected == t'2018-03-22T12:11:14.1Z']", 86 | "[artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q=']", 87 | "[foo:bar=1] REPEATS 9 TIMES", 88 | "[network-traffic:start = '2018-04-20T12:36:24.558Z']", 89 | "( [(network-traffic:dst_port IN(443,6443,8443) AND network-traffic:src_packets != 0) ])", # Misplaced whitespace 90 | "[file:hashes[*] = '8665c8d477534008b3058b72e2dae8ae']", 91 | ] 92 | 93 | 94 | @pytest.mark.parametrize("test_input", PASS_CASES) 95 | def test_pass_patterns(test_input): 96 | """ 97 | Validate that patterns pass as expected. 98 | """ 99 | pass_test = validate(test_input, stix_version='2.0', print_errs=True) 100 | assert pass_test is True 101 | -------------------------------------------------------------------------------- /stix2patterns/test/v21/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/test/v21/__init__.py -------------------------------------------------------------------------------- /stix2patterns/test/v21/spec_examples.txt: -------------------------------------------------------------------------------- 1 | [file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f'] 2 | [email-message:from_ref.value MATCHES '.+\\@example\\.com$' AND email-message:body_multipart[*].body_raw_ref.name MATCHES '^Final Report.+\\.exe$'] 3 | [file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f' AND file:mime_type = 'application/x-pdf'] 4 | [file:hashes.'SHA-256' = 'bf07a7fbb825fc0aae7bf4a1177b2b31fcf8a3feeaf7092761e18c859ee52a9c' OR file:hashes.MD5 = 'cead3f77f6cda6ec00f57d76c9a6879f'] AND [file:hashes.'SHA-256' = 'aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f'] 5 | ([file:hashes.MD5 = '79054025255fb1a26e4bc422aef54eb4'] FOLLOWEDBY [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\foo\\bar']) WITHIN 300 SECONDS 6 | [user-account:account_type = 'unix' AND user-account:user_id = '1007' AND user-account:account_login = 'Peter'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1008' AND user-account:account_login = 'Paul'] AND [user-account:account_type = 'unix' AND user-account:user_id = '1009' AND user-account:account_login = 'Mary'] 7 | [artifact:mime_type = 'application/vnd.tcpdump.pcap' AND artifact:payload_bin MATCHES '\\xd4\\xc3\\xb2\\xa1\\x02\\x00\\x04\\x00'] 8 | [file:name = 'foo.dll' AND file:parent_directory_ref.path = 'C:\\Windows\\System32'] 9 | [file:extensions.'windows-pebinary-ext'.sections[*].entropy > 7.0] 10 | [file:mime_type = 'image/bmp' AND file:magic_number_hex = h'ffd8'] 11 | [network-traffic:dst_ref.type = 'ipv4-addr' AND network-traffic:dst_ref.value = '203.0.113.33/32'] 12 | [network-traffic:dst_ref.type = 'domain-name' AND network-traffic:dst_ref.value = 'example.com'] REPEATS 5 TIMES WITHIN 1800 SECONDS 13 | [domain-name:value = 'www.5z8.info' AND domain-name:resolves_to_refs[*].value = '198.51.100.1/32'] 14 | [url:value = 'http://example.com/foo' OR url:value = 'http://example.com/bar'] 15 | [x509-certificate:issuer = 'CN=WEBMAIL' AND x509-certificate:serial_number = '4c:0b:1d:19:74:86:a7:66:b4:1a:bf:40:27:21:76:28'] 16 | [windows-registry-key:key = 'HKEY_CURRENT_USER\\Software\\CryptoLocker\\Files' OR windows-registry-key:key = 'HKEY_CURRENT_USER\\Software\\Microsoft\\CurrentVersion\\Run\\CryptoLocker_0388'] 17 | [(file:name = 'pdf.exe' OR file:size = '371712') AND file:created = t'2014-01-13T07:03:17Z'] 18 | [email-message:sender_ref.value = 'jdoe@example.com' AND email-message:subject = 'Conference Info'] 19 | [x-usb-device:usbdrive.serial_number = '575833314133343231313937'] 20 | [process:command_line MATCHES '^.+>-add GlobalSign.cer -c -s -r localMachine Root$'] FOLLOWEDBY [process:command_line MATCHES'^.+>-add GlobalSign.cer -c -s -r localMachineTrustedPublisher$'] WITHIN 300 SECONDS 21 | [network-traffic:dst_ref.value ISSUBSET '2001:0db8:dead:beef:0000:0000:0000:0000/64'] 22 | ([file:name = 'foo.dll'] AND [win-registry-key:key = 'HKEY_LOCAL_MACHINE\\foo\\bar']) OR [process:name = 'fooproc' OR process:name = 'procfoo'] 23 | [file:hashes.MD5 = 'cead3f77f6cda6ec00f57d76c9a69faa'] 24 | -------------------------------------------------------------------------------- /stix2patterns/test/v21/test_inspector.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from stix2patterns.inspector import INDEX_STAR 4 | from stix2patterns.v21.pattern import Pattern 5 | 6 | 7 | @pytest.mark.parametrize(u"pattern,expected_qualifiers", [ 8 | (u"[foo:bar = 1]", set()), 9 | (u"[foo:bar = 1] REPEATS 5 TIMES", set([u"REPEATS 5 TIMES"])), 10 | (u"[foo:bar = 1] WITHIN 10.3 SECONDS", set([u"WITHIN 10.3 SECONDS"])), 11 | (u"[foo:bar = 1] WITHIN 123 SECONDS", set([u"WITHIN 123 SECONDS"])), 12 | (u"[foo:bar = 1] START t'1932-11-12T15:42:15Z' STOP t'1964-10-23T21:12:26Z'", 13 | set([u"START t'1932-11-12T15:42:15Z' STOP t'1964-10-23T21:12:26Z'"])), 14 | (u"[foo:bar = 1] REPEATS 1 TIMES AND [foo:baz = 2] WITHIN 1.23 SECONDS", 15 | set([u"REPEATS 1 TIMES", u"WITHIN 1.23 SECONDS"])), 16 | (u"([foo:bar = 1] START t'1932-11-12T15:42:15Z' STOP t'1964-10-23T21:12:26Z' AND [foo:abc < h'12ab']) WITHIN 22 SECONDS " 17 | u"OR [frob:baz NOT IN (1,2,3)] REPEATS 31 TIMES", 18 | set([u"START t'1932-11-12T15:42:15Z' STOP t'1964-10-23T21:12:26Z'", 19 | u"WITHIN 22 SECONDS", u"REPEATS 31 TIMES"])) 20 | ]) 21 | def test_qualifiers(pattern, expected_qualifiers): 22 | compiled_pattern = Pattern(pattern) 23 | pattern_data = compiled_pattern.inspect() 24 | 25 | assert pattern_data.qualifiers == expected_qualifiers 26 | 27 | 28 | @pytest.mark.parametrize(u"pattern,expected_obs_ops", [ 29 | (u"[foo:bar = 1]", set()), 30 | (u"[foo:bar = 1] AND [foo:baz > 25.2]", set([u"AND"])), 31 | (u"[foo:bar = 1] OR [foo:baz != 'hello']", set([u"OR"])), 32 | (u"[foo:bar = 1] FOLLOWEDBY [foo:baz IN (1,2,3)]", set([u"FOLLOWEDBY"])), 33 | (u"[foo:bar = 1] AND [foo:baz = 22] OR [foo:abc = '123']", set([u"AND", u"OR"])), 34 | (u"[foo:bar = 1] OR ([foo:baz = false] FOLLOWEDBY [frob:abc LIKE '123']) WITHIN 46.1 SECONDS", 35 | set([u"OR", u"FOLLOWEDBY"])) 36 | ]) 37 | def test_observation_ops(pattern, expected_obs_ops): 38 | compiled_pattern = Pattern(pattern) 39 | pattern_data = compiled_pattern.inspect() 40 | 41 | assert pattern_data.observation_ops == expected_obs_ops 42 | 43 | 44 | @pytest.mark.parametrize(u"pattern,expected_comparisons", [ 45 | (u"[foo:bar = 1]", {u"foo": [([u"bar"], u"=", u"1")]}), 46 | (u"[foo:bar=1 AND foo:baz=2]", {u"foo": [([u"bar"], u"=", u"1"), ([u"baz"], u"=", u"2")]}), 47 | (u"[foo:bar NOT !=1 OR bar:foo<12.3]", { 48 | u"foo": [([u"bar"], u"NOT !=", u"1")], 49 | u"bar": [([u"foo"], u"<", u"12.3")] 50 | }), 51 | (u"[foo:bar=1] OR [foo:baz MATCHES '123\\\\d+']", { 52 | u"foo": [([u"bar"], u"=", u"1"), ([u"baz"], u"MATCHES", u"'123\\\\d+'")] 53 | }), 54 | (u"[foo:bar=1 AND bar:foo NOT >33] REPEATS 12 TIMES OR " 55 | u" ([baz:bar ISSUBSET '1234'] FOLLOWEDBY [baz:quux NOT LIKE 'a_cd'])", 56 | { 57 | u"foo": [([u"bar"], u"=", u"1")], 58 | u"bar": [([u"foo"], u"NOT >", u"33")], 59 | u"baz": [([u"bar"], u"ISSUBSET", u"'1234'"), ([u"quux"], u"NOT LIKE", u"'a_cd'")] 60 | }), 61 | (u"[obj-type:a.b[*][1].'c-d' NOT ISSUPERSET '1.2.3.4/16']", { 62 | u"obj-type": [([u"a", u"b", INDEX_STAR, 1, u"c-d"], u"NOT ISSUPERSET", u"'1.2.3.4/16'")] 63 | }), 64 | ]) 65 | def test_comparisons(pattern, expected_comparisons): 66 | compiled_pattern = Pattern(pattern) 67 | pattern_data = compiled_pattern.inspect() 68 | 69 | assert pattern_data.comparisons == expected_comparisons 70 | -------------------------------------------------------------------------------- /stix2patterns/test/v21/test_validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test cases for stix2patterns/validator.py. 3 | """ 4 | import os 5 | 6 | import pytest 7 | 8 | from stix2patterns.validator import validate 9 | 10 | TEST_CASE_FILE = os.path.join(os.path.dirname(__file__), 'spec_examples.txt') 11 | with open(TEST_CASE_FILE) as f: 12 | SPEC_CASES = [x.strip() for x in f.readlines()] 13 | 14 | 15 | @pytest.mark.parametrize("test_input", SPEC_CASES) 16 | def test_spec_patterns(test_input): 17 | """ 18 | Validate patterns from STIX 2.1 Patterning spec. 19 | """ 20 | pass_test = validate(test_input, stix_version='2.1', print_errs=True) 21 | assert pass_test is True 22 | 23 | 24 | FAIL_CASES = [ 25 | ("file:size = 1280", # Does not use square brackets 26 | "FAIL: Error found at line 1:0. input is missing square brackets"), 27 | ("[file:size = ]", # Missing value 28 | "FAIL: Error found at line 1:13. mismatched input ']'"), 29 | ("[file:hashes.MD5 = cead3f77f6cda6ec00f57d76c9a6879f]", # No quotes around string 30 | "FAIL: Error found at line 1:19. mismatched input 'cead3f77f6cda6ec00f57d76c9a6879f'"), 31 | ("[file.size = 1280]", # Use period instead of colon 32 | "FAIL: Error found at line 1:5. no viable alternative at input 'file.'"), 33 | ("[file:name MATCHES /.*\\.dll/]", # Quotes around regular expression 34 | "FAIL: Error found at line 1:19. mismatched input '/' expecting StringLiteral"), 35 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN ]", # Missing Qualifier value 36 | "FAIL: Error found at line 1:63. mismatched input ']' expecting {IntPosLiteral, FloatPosLiteral}"), 37 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 HOURS]", # SECONDS is the only valid time unit 38 | "FAIL: Error found at line 1:65. mismatched input 'HOURS' expecting 'SECONDS'"), 39 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN -5 SECONDS]", # Negative integer is invalid 40 | "FAIL: Error found at line 1:63. mismatched input '-5' expecting {IntPosLiteral, FloatPosLiteral}"), 41 | ("[network-traffic:dst_ref.value ISSUBSET ]", # Missing second Comparison operand 42 | "FAIL: Error found at line 1:40. missing StringLiteral at ']'"), 43 | ("[file:hashes.MD5 =? 'cead3f77f6cda6ec00f57d76c9a6879f']", # '=?' isn't a valid operator 44 | "FAIL: Error found at line 1:18. extraneous input '?'"), 45 | ("[x_whatever:detected == t'2457-73-22T32:81:84.1Z']", # Not a valid date 46 | "FAIL: Error found at line 1:24. extraneous input 't'"), 47 | ("[artifact:payload_bin = b'====']", # Not valid Base64 48 | "FAIL: Error found at line 1:24. extraneous input 'b'"), 49 | ("[foo:bar=1] within 2 seconds", # keywords must be uppercase 50 | "FAIL: Error found at line 1:12. mismatched input 'within' expecting "), 51 | ("[file:hashes.'SHA-256' = 'f00']", # Malformed hash value 52 | "FAIL: 'f00' is not a valid SHA-256 hash"), 53 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 SECONDS WITHIN 6 SECONDS", 54 | "FAIL: Duplicate qualifier type encountered: WITHIN"), 55 | ("([win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] REPEATS 2 TIMES REPEATS 3 TIMES)", 56 | "FAIL: Duplicate qualifier type encountered: REPEATS"), 57 | ("[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] " 58 | "START t'2016-06-01T01:30:00.123Z' STOP t'2016-06-01T02:20:00.123Z' " 59 | "START t'2016-06-01T01:30:00.123Z' STOP t'2016-06-01T02:20:00.123Z'", 60 | "FAIL: Duplicate qualifier type encountered: STARTSTOP"), 61 | # TODO: add more failing test cases. 62 | ] 63 | 64 | 65 | @pytest.mark.parametrize("test_input,test_output", FAIL_CASES) 66 | def test_fail_patterns(test_input, test_output): 67 | """ 68 | Validate that patterns fail as expected. 69 | """ 70 | pass_test, errors = validate(test_input, stix_version='2.1', ret_errs=True, print_errs=True) 71 | assert errors[0].startswith(test_output) 72 | assert pass_test is False 73 | 74 | 75 | PASS_CASES = [ 76 | "[file:size = 1280]", 77 | "[file:size != 1280]", 78 | "[file:size < 1024]", 79 | "[file:size <= 1024]", 80 | "[file:size > 1024]", 81 | "[file:size >= 1024]", 82 | "[file:file_name = 'my_file_name']", 83 | "[file:extended_properties.'ntfs-ext'.sid = '234']", 84 | r"[emailaddr:value MATCHES '.+\\@ibm\\.com$' OR file:name MATCHES '^Final Report.+\\.exe$']", 85 | "[ipv4addr:value ISSUBSET '192.168.0.1/24']", 86 | "[ipv4addr:value NOT ISSUBSET '192.168.0.1/24']", 87 | "[user-account:value = 'Peter'] AND [user-account:value != 'Paul'] AND [user-account:value = 'Mary'] WITHIN 5 SECONDS", 88 | "[file:file_system_properties.file_name LIKE 'name%']", 89 | "[file:file_name IN ('test.txt', 'test2.exe', 'README')]", 90 | "[file:size IN (1024, 2048, 4096)]", 91 | "[network-connection:extended_properties[0].source_payload MATCHES 'dGVzdHRlc3R0ZXN0']", 92 | "[win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] WITHIN 5 SECONDS", 93 | "[x_whatever:detected == t'2018-03-22T12:11:14.1Z']", 94 | "[artifact:payload_bin = b'dGhpcyBpcyBhIHRlc3Q=']", 95 | "[foo:bar=1] REPEATS 9 TIMES", 96 | "[network-traffic:start = '2018-04-20T12:36:24.558Z']", 97 | "( [(network-traffic:dst_port IN(443,6443,8443) AND network-traffic:src_packets != 0) ])", # Misplaced whitespace 98 | "([win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar']) WITHIN 5 SECONDS WITHIN 6 SECONDS", 99 | "([win-registry-key:key = 'hkey_local_machine\\\\foo\\\\bar'] REPEATS 2 TIMES) REPEATS 2 TIMES", 100 | "[network-traffic:src_port = 37020 AND user-account:user_id = 'root'] " 101 | "START t'2016-06-01T01:30:00.123Z' STOP t'2016-06-01T02:20:00.123Z' OR " 102 | "[ipv4-addr:value = '192.168.122.83'] " 103 | "START t'2016-06-01T03:55:00.123Z' STOP t'2016-06-01T04:30:24.743Z'", 104 | "[file:hashes[*] = '8665c8d477534008b3058b72e2dae8ae']", 105 | ] 106 | 107 | 108 | @pytest.mark.parametrize("test_input", PASS_CASES) 109 | def test_pass_patterns(test_input): 110 | """ 111 | Validate that patterns pass as expected. 112 | """ 113 | pass_test = validate(test_input, stix_version='2.1', print_errs=True) 114 | assert pass_test is True 115 | -------------------------------------------------------------------------------- /stix2patterns/v20/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/v20/__init__.py -------------------------------------------------------------------------------- /stix2patterns/v20/grammars/STIXPatternLexer.py: -------------------------------------------------------------------------------- 1 | # Generated from STIXPattern.g4 by ANTLR 4.9.2 2 | from antlr4 import * 3 | from io import StringIO 4 | import sys 5 | if sys.version_info[1] > 5: 6 | from typing import TextIO 7 | else: 8 | from typing.io import TextIO 9 | 10 | 11 | 12 | def serializedATN(): 13 | with StringIO() as buf: 14 | buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\67") 15 | buf.write("\u01ef\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") 16 | buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") 17 | buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") 18 | buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") 19 | buf.write("\4\31\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36") 20 | buf.write("\t\36\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%") 21 | buf.write("\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.") 22 | buf.write("\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64") 23 | buf.write("\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\3\2\3") 24 | buf.write("\2\3\2\3\2\7\2x\n\2\f\2\16\2{\13\2\5\2}\n\2\3\3\5\3\u0080") 25 | buf.write("\n\3\3\3\3\3\3\3\7\3\u0085\n\3\f\3\16\3\u0088\13\3\5\3") 26 | buf.write("\u008a\n\3\3\4\3\4\7\4\u008e\n\4\f\4\16\4\u0091\13\4\3") 27 | buf.write("\4\3\4\6\4\u0095\n\4\r\4\16\4\u0096\3\5\5\5\u009a\n\5") 28 | buf.write("\3\5\7\5\u009d\n\5\f\5\16\5\u00a0\13\5\3\5\3\5\6\5\u00a4") 29 | buf.write("\n\5\r\5\16\5\u00a5\3\6\3\6\3\6\7\6\u00ab\n\6\f\6\16\6") 30 | buf.write("\u00ae\13\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\7\7\u00b9") 31 | buf.write("\n\7\f\7\16\7\u00bc\13\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3") 32 | buf.write("\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\5\7\u00cf\n\7\3") 33 | buf.write("\7\3\7\3\b\3\b\3\b\3\b\3\b\3\b\7\b\u00d9\n\b\f\b\16\b") 34 | buf.write("\u00dc\13\b\3\b\3\b\3\t\3\t\5\t\u00e2\n\t\3\n\3\n\3\n") 35 | buf.write("\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\5\n\u00ef\n\n\3\n\3\n") 36 | buf.write("\3\n\3\n\3\n\3\n\3\n\5\n\u00f8\n\n\3\n\3\n\3\n\3\n\3\n") 37 | buf.write("\5\n\u00ff\n\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\5\n\u0109") 38 | buf.write("\n\n\3\n\3\n\6\n\u010d\n\n\r\n\16\n\u010e\5\n\u0111\n") 39 | buf.write("\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\r\3\r") 40 | buf.write("\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16") 41 | buf.write("\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\20") 42 | buf.write("\3\20\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\21") 43 | buf.write("\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22") 44 | buf.write("\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\25") 45 | buf.write("\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3\27") 46 | buf.write("\3\27\3\27\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30") 47 | buf.write("\3\30\3\31\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32\3\32") 48 | buf.write("\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\33") 49 | buf.write("\3\34\3\34\3\34\3\34\3\34\3\34\3\35\3\35\7\35\u018a\n") 50 | buf.write("\35\f\35\16\35\u018d\13\35\3\36\3\36\7\36\u0191\n\36\f") 51 | buf.write("\36\16\36\u0194\13\36\3\37\3\37\3\37\5\37\u0199\n\37\3") 52 | buf.write(" \3 \3 \3 \5 \u019f\n \3!\3!\3\"\3\"\3\"\3#\3#\3$\3$\3") 53 | buf.write("$\3%\3%\3&\3&\3\'\3\'\3(\3(\3)\3)\3*\3*\3+\3+\3,\3,\3") 54 | buf.write("-\3-\3.\3.\3/\3/\3\60\3\60\3\61\3\61\3\62\3\62\3\63\3") 55 | buf.write("\63\3\64\3\64\3\64\3\65\3\65\3\66\6\66\u01cf\n\66\r\66") 56 | buf.write("\16\66\u01d0\3\66\3\66\3\67\3\67\3\67\3\67\7\67\u01d9") 57 | buf.write("\n\67\f\67\16\67\u01dc\13\67\3\67\3\67\3\67\3\67\3\67") 58 | buf.write("\38\38\38\38\78\u01e7\n8\f8\168\u01ea\138\38\38\39\39") 59 | buf.write("\3\u01da\2:\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25") 60 | buf.write("\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+") 61 | buf.write("\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E") 62 | buf.write("$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\2g\2i\2k\64") 63 | buf.write("m\65o\66q\67\3\2\21\3\2\63;\3\2\62;\4\2))^^\3\2\62\64") 64 | buf.write("\3\2\63\64\3\2\62\63\3\2\62\65\3\2\62\67\5\2C\\aac|\6") 65 | buf.write("\2\62;C\\aac|\7\2//\62;C\\aac|\5\2\62;CHch\6\2--\61;C") 66 | buf.write("\\c|\f\2\13\17\"\"\u0087\u0087\u00a2\u00a2\u1682\u1682") 67 | buf.write("\u2002\u200c\u202a\u202b\u2031\u2031\u2061\u2061\u3002") 68 | buf.write("\u3002\4\2\f\f\17\17\2\u020b\2\3\3\2\2\2\2\5\3\2\2\2\2") 69 | buf.write("\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3") 70 | buf.write("\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2") 71 | buf.write("\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2") 72 | buf.write("\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2") 73 | buf.write("\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63") 74 | buf.write("\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2") 75 | buf.write("\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2") 76 | buf.write("\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3") 77 | buf.write("\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y") 78 | buf.write("\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2") 79 | buf.write("c\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2") 80 | buf.write("\3s\3\2\2\2\5\177\3\2\2\2\7\u008b\3\2\2\2\t\u0099\3\2") 81 | buf.write("\2\2\13\u00a7\3\2\2\2\r\u00b1\3\2\2\2\17\u00d2\3\2\2\2") 82 | buf.write("\21\u00e1\3\2\2\2\23\u00e3\3\2\2\2\25\u0115\3\2\2\2\27") 83 | buf.write("\u0119\3\2\2\2\31\u011c\3\2\2\2\33\u0120\3\2\2\2\35\u012b") 84 | buf.write("\3\2\2\2\37\u0130\3\2\2\2!\u0138\3\2\2\2#\u0143\3\2\2") 85 | buf.write("\2%\u014c\3\2\2\2\'\u0151\3\2\2\2)\u0154\3\2\2\2+\u015a") 86 | buf.write("\3\2\2\2-\u015f\3\2\2\2/\u0167\3\2\2\2\61\u016c\3\2\2") 87 | buf.write("\2\63\u0172\3\2\2\2\65\u0179\3\2\2\2\67\u0181\3\2\2\2") 88 | buf.write("9\u0187\3\2\2\2;\u018e\3\2\2\2=\u0198\3\2\2\2?\u019e\3") 89 | buf.write("\2\2\2A\u01a0\3\2\2\2C\u01a2\3\2\2\2E\u01a5\3\2\2\2G\u01a7") 90 | buf.write("\3\2\2\2I\u01aa\3\2\2\2K\u01ac\3\2\2\2M\u01ae\3\2\2\2") 91 | buf.write("O\u01b0\3\2\2\2Q\u01b2\3\2\2\2S\u01b4\3\2\2\2U\u01b6\3") 92 | buf.write("\2\2\2W\u01b8\3\2\2\2Y\u01ba\3\2\2\2[\u01bc\3\2\2\2]\u01be") 93 | buf.write("\3\2\2\2_\u01c0\3\2\2\2a\u01c2\3\2\2\2c\u01c4\3\2\2\2") 94 | buf.write("e\u01c6\3\2\2\2g\u01c8\3\2\2\2i\u01cb\3\2\2\2k\u01ce\3") 95 | buf.write("\2\2\2m\u01d4\3\2\2\2o\u01e2\3\2\2\2q\u01ed\3\2\2\2s|") 96 | buf.write("\7/\2\2t}\7\62\2\2uy\t\2\2\2vx\t\3\2\2wv\3\2\2\2x{\3\2") 97 | buf.write("\2\2yw\3\2\2\2yz\3\2\2\2z}\3\2\2\2{y\3\2\2\2|t\3\2\2\2") 98 | buf.write("|u\3\2\2\2}\4\3\2\2\2~\u0080\7-\2\2\177~\3\2\2\2\177\u0080") 99 | buf.write("\3\2\2\2\u0080\u0089\3\2\2\2\u0081\u008a\7\62\2\2\u0082") 100 | buf.write("\u0086\t\2\2\2\u0083\u0085\t\3\2\2\u0084\u0083\3\2\2\2") 101 | buf.write("\u0085\u0088\3\2\2\2\u0086\u0084\3\2\2\2\u0086\u0087\3") 102 | buf.write("\2\2\2\u0087\u008a\3\2\2\2\u0088\u0086\3\2\2\2\u0089\u0081") 103 | buf.write("\3\2\2\2\u0089\u0082\3\2\2\2\u008a\6\3\2\2\2\u008b\u008f") 104 | buf.write("\7/\2\2\u008c\u008e\t\3\2\2\u008d\u008c\3\2\2\2\u008e") 105 | buf.write("\u0091\3\2\2\2\u008f\u008d\3\2\2\2\u008f\u0090\3\2\2\2") 106 | buf.write("\u0090\u0092\3\2\2\2\u0091\u008f\3\2\2\2\u0092\u0094\7") 107 | buf.write("\60\2\2\u0093\u0095\t\3\2\2\u0094\u0093\3\2\2\2\u0095") 108 | buf.write("\u0096\3\2\2\2\u0096\u0094\3\2\2\2\u0096\u0097\3\2\2\2") 109 | buf.write("\u0097\b\3\2\2\2\u0098\u009a\7-\2\2\u0099\u0098\3\2\2") 110 | buf.write("\2\u0099\u009a\3\2\2\2\u009a\u009e\3\2\2\2\u009b\u009d") 111 | buf.write("\t\3\2\2\u009c\u009b\3\2\2\2\u009d\u00a0\3\2\2\2\u009e") 112 | buf.write("\u009c\3\2\2\2\u009e\u009f\3\2\2\2\u009f\u00a1\3\2\2\2") 113 | buf.write("\u00a0\u009e\3\2\2\2\u00a1\u00a3\7\60\2\2\u00a2\u00a4") 114 | buf.write("\t\3\2\2\u00a3\u00a2\3\2\2\2\u00a4\u00a5\3\2\2\2\u00a5") 115 | buf.write("\u00a3\3\2\2\2\u00a5\u00a6\3\2\2\2\u00a6\n\3\2\2\2\u00a7") 116 | buf.write("\u00a8\7j\2\2\u00a8\u00ac\5I%\2\u00a9\u00ab\5g\64\2\u00aa") 117 | buf.write("\u00a9\3\2\2\2\u00ab\u00ae\3\2\2\2\u00ac\u00aa\3\2\2\2") 118 | buf.write("\u00ac\u00ad\3\2\2\2\u00ad\u00af\3\2\2\2\u00ae\u00ac\3") 119 | buf.write("\2\2\2\u00af\u00b0\5I%\2\u00b0\f\3\2\2\2\u00b1\u00b2\7") 120 | buf.write("d\2\2\u00b2\u00ba\5I%\2\u00b3\u00b4\5i\65\2\u00b4\u00b5") 121 | buf.write("\5i\65\2\u00b5\u00b6\5i\65\2\u00b6\u00b7\5i\65\2\u00b7") 122 | buf.write("\u00b9\3\2\2\2\u00b8\u00b3\3\2\2\2\u00b9\u00bc\3\2\2\2") 123 | buf.write("\u00ba\u00b8\3\2\2\2\u00ba\u00bb\3\2\2\2\u00bb\u00ce\3") 124 | buf.write("\2\2\2\u00bc\u00ba\3\2\2\2\u00bd\u00be\5i\65\2\u00be\u00bf") 125 | buf.write("\5i\65\2\u00bf\u00c0\5i\65\2\u00c0\u00c1\5i\65\2\u00c1") 126 | buf.write("\u00cf\3\2\2\2\u00c2\u00c3\5i\65\2\u00c3\u00c4\5i\65\2") 127 | buf.write("\u00c4\u00c5\5i\65\2\u00c5\u00c6\3\2\2\2\u00c6\u00c7\7") 128 | buf.write("?\2\2\u00c7\u00cf\3\2\2\2\u00c8\u00c9\5i\65\2\u00c9\u00ca") 129 | buf.write("\5i\65\2\u00ca\u00cb\3\2\2\2\u00cb\u00cc\7?\2\2\u00cc") 130 | buf.write("\u00cd\7?\2\2\u00cd\u00cf\3\2\2\2\u00ce\u00bd\3\2\2\2") 131 | buf.write("\u00ce\u00c2\3\2\2\2\u00ce\u00c8\3\2\2\2\u00cf\u00d0\3") 132 | buf.write("\2\2\2\u00d0\u00d1\5I%\2\u00d1\16\3\2\2\2\u00d2\u00da") 133 | buf.write("\5I%\2\u00d3\u00d9\n\4\2\2\u00d4\u00d5\7^\2\2\u00d5\u00d9") 134 | buf.write("\7)\2\2\u00d6\u00d7\7^\2\2\u00d7\u00d9\7^\2\2\u00d8\u00d3") 135 | buf.write("\3\2\2\2\u00d8\u00d4\3\2\2\2\u00d8\u00d6\3\2\2\2\u00d9") 136 | buf.write("\u00dc\3\2\2\2\u00da\u00d8\3\2\2\2\u00da\u00db\3\2\2\2") 137 | buf.write("\u00db\u00dd\3\2\2\2\u00dc\u00da\3\2\2\2\u00dd\u00de\5") 138 | buf.write("I%\2\u00de\20\3\2\2\2\u00df\u00e2\5/\30\2\u00e0\u00e2") 139 | buf.write("\5\61\31\2\u00e1\u00df\3\2\2\2\u00e1\u00e0\3\2\2\2\u00e2") 140 | buf.write("\22\3\2\2\2\u00e3\u00e4\7v\2\2\u00e4\u00e5\5I%\2\u00e5") 141 | buf.write("\u00e6\t\3\2\2\u00e6\u00e7\t\3\2\2\u00e7\u00e8\t\3\2\2") 142 | buf.write("\u00e8\u00e9\t\3\2\2\u00e9\u00ee\5[.\2\u00ea\u00eb\7\62") 143 | buf.write("\2\2\u00eb\u00ef\t\2\2\2\u00ec\u00ed\7\63\2\2\u00ed\u00ef") 144 | buf.write("\t\5\2\2\u00ee\u00ea\3\2\2\2\u00ee\u00ec\3\2\2\2\u00ef") 145 | buf.write("\u00f0\3\2\2\2\u00f0\u00f7\5[.\2\u00f1\u00f2\7\62\2\2") 146 | buf.write("\u00f2\u00f8\t\2\2\2\u00f3\u00f4\t\6\2\2\u00f4\u00f8\t") 147 | buf.write("\3\2\2\u00f5\u00f6\7\65\2\2\u00f6\u00f8\t\7\2\2\u00f7") 148 | buf.write("\u00f1\3\2\2\2\u00f7\u00f3\3\2\2\2\u00f7\u00f5\3\2\2\2") 149 | buf.write("\u00f8\u00f9\3\2\2\2\u00f9\u00fe\7V\2\2\u00fa\u00fb\t") 150 | buf.write("\7\2\2\u00fb\u00ff\t\3\2\2\u00fc\u00fd\7\64\2\2\u00fd") 151 | buf.write("\u00ff\t\b\2\2\u00fe\u00fa\3\2\2\2\u00fe\u00fc\3\2\2\2") 152 | buf.write("\u00ff\u0100\3\2\2\2\u0100\u0101\5K&\2\u0101\u0102\t\t") 153 | buf.write("\2\2\u0102\u0103\t\3\2\2\u0103\u0108\5K&\2\u0104\u0105") 154 | buf.write("\t\t\2\2\u0105\u0109\t\3\2\2\u0106\u0107\78\2\2\u0107") 155 | buf.write("\u0109\7\62\2\2\u0108\u0104\3\2\2\2\u0108\u0106\3\2\2") 156 | buf.write("\2\u0109\u0110\3\2\2\2\u010a\u010c\5M\'\2\u010b\u010d") 157 | buf.write("\t\3\2\2\u010c\u010b\3\2\2\2\u010d\u010e\3\2\2\2\u010e") 158 | buf.write("\u010c\3\2\2\2\u010e\u010f\3\2\2\2\u010f\u0111\3\2\2\2") 159 | buf.write("\u0110\u010a\3\2\2\2\u0110\u0111\3\2\2\2\u0111\u0112\3") 160 | buf.write("\2\2\2\u0112\u0113\7\\\2\2\u0113\u0114\5I%\2\u0114\24") 161 | buf.write("\3\2\2\2\u0115\u0116\7C\2\2\u0116\u0117\7P\2\2\u0117\u0118") 162 | buf.write("\7F\2\2\u0118\26\3\2\2\2\u0119\u011a\7Q\2\2\u011a\u011b") 163 | buf.write("\7T\2\2\u011b\30\3\2\2\2\u011c\u011d\7P\2\2\u011d\u011e") 164 | buf.write("\7Q\2\2\u011e\u011f\7V\2\2\u011f\32\3\2\2\2\u0120\u0121") 165 | buf.write("\7H\2\2\u0121\u0122\7Q\2\2\u0122\u0123\7N\2\2\u0123\u0124") 166 | buf.write("\7N\2\2\u0124\u0125\7Q\2\2\u0125\u0126\7Y\2\2\u0126\u0127") 167 | buf.write("\7G\2\2\u0127\u0128\7F\2\2\u0128\u0129\7D\2\2\u0129\u012a") 168 | buf.write("\7[\2\2\u012a\34\3\2\2\2\u012b\u012c\7N\2\2\u012c\u012d") 169 | buf.write("\7K\2\2\u012d\u012e\7M\2\2\u012e\u012f\7G\2\2\u012f\36") 170 | buf.write("\3\2\2\2\u0130\u0131\7O\2\2\u0131\u0132\7C\2\2\u0132\u0133") 171 | buf.write("\7V\2\2\u0133\u0134\7E\2\2\u0134\u0135\7J\2\2\u0135\u0136") 172 | buf.write("\7G\2\2\u0136\u0137\7U\2\2\u0137 \3\2\2\2\u0138\u0139") 173 | buf.write("\7K\2\2\u0139\u013a\7U\2\2\u013a\u013b\7U\2\2\u013b\u013c") 174 | buf.write("\7W\2\2\u013c\u013d\7R\2\2\u013d\u013e\7G\2\2\u013e\u013f") 175 | buf.write("\7T\2\2\u013f\u0140\7U\2\2\u0140\u0141\7G\2\2\u0141\u0142") 176 | buf.write("\7V\2\2\u0142\"\3\2\2\2\u0143\u0144\7K\2\2\u0144\u0145") 177 | buf.write("\7U\2\2\u0145\u0146\7U\2\2\u0146\u0147\7W\2\2\u0147\u0148") 178 | buf.write("\7D\2\2\u0148\u0149\7U\2\2\u0149\u014a\7G\2\2\u014a\u014b") 179 | buf.write("\7V\2\2\u014b$\3\2\2\2\u014c\u014d\7N\2\2\u014d\u014e") 180 | buf.write("\7C\2\2\u014e\u014f\7U\2\2\u014f\u0150\7V\2\2\u0150&\3") 181 | buf.write("\2\2\2\u0151\u0152\7K\2\2\u0152\u0153\7P\2\2\u0153(\3") 182 | buf.write("\2\2\2\u0154\u0155\7U\2\2\u0155\u0156\7V\2\2\u0156\u0157") 183 | buf.write("\7C\2\2\u0157\u0158\7T\2\2\u0158\u0159\7V\2\2\u0159*\3") 184 | buf.write("\2\2\2\u015a\u015b\7U\2\2\u015b\u015c\7V\2\2\u015c\u015d") 185 | buf.write("\7Q\2\2\u015d\u015e\7R\2\2\u015e,\3\2\2\2\u015f\u0160") 186 | buf.write("\7U\2\2\u0160\u0161\7G\2\2\u0161\u0162\7E\2\2\u0162\u0163") 187 | buf.write("\7Q\2\2\u0163\u0164\7P\2\2\u0164\u0165\7F\2\2\u0165\u0166") 188 | buf.write("\7U\2\2\u0166.\3\2\2\2\u0167\u0168\7v\2\2\u0168\u0169") 189 | buf.write("\7t\2\2\u0169\u016a\7w\2\2\u016a\u016b\7g\2\2\u016b\60") 190 | buf.write("\3\2\2\2\u016c\u016d\7h\2\2\u016d\u016e\7c\2\2\u016e\u016f") 191 | buf.write("\7n\2\2\u016f\u0170\7u\2\2\u0170\u0171\7g\2\2\u0171\62") 192 | buf.write("\3\2\2\2\u0172\u0173\7Y\2\2\u0173\u0174\7K\2\2\u0174\u0175") 193 | buf.write("\7V\2\2\u0175\u0176\7J\2\2\u0176\u0177\7K\2\2\u0177\u0178") 194 | buf.write("\7P\2\2\u0178\64\3\2\2\2\u0179\u017a\7T\2\2\u017a\u017b") 195 | buf.write("\7G\2\2\u017b\u017c\7R\2\2\u017c\u017d\7G\2\2\u017d\u017e") 196 | buf.write("\7C\2\2\u017e\u017f\7V\2\2\u017f\u0180\7U\2\2\u0180\66") 197 | buf.write("\3\2\2\2\u0181\u0182\7V\2\2\u0182\u0183\7K\2\2\u0183\u0184") 198 | buf.write("\7O\2\2\u0184\u0185\7G\2\2\u0185\u0186\7U\2\2\u01868\3") 199 | buf.write("\2\2\2\u0187\u018b\t\n\2\2\u0188\u018a\t\13\2\2\u0189") 200 | buf.write("\u0188\3\2\2\2\u018a\u018d\3\2\2\2\u018b\u0189\3\2\2\2") 201 | buf.write("\u018b\u018c\3\2\2\2\u018c:\3\2\2\2\u018d\u018b\3\2\2") 202 | buf.write("\2\u018e\u0192\t\n\2\2\u018f\u0191\t\f\2\2\u0190\u018f") 203 | buf.write("\3\2\2\2\u0191\u0194\3\2\2\2\u0192\u0190\3\2\2\2\u0192") 204 | buf.write("\u0193\3\2\2\2\u0193<\3\2\2\2\u0194\u0192\3\2\2\2\u0195") 205 | buf.write("\u0199\7?\2\2\u0196\u0197\7?\2\2\u0197\u0199\7?\2\2\u0198") 206 | buf.write("\u0195\3\2\2\2\u0198\u0196\3\2\2\2\u0199>\3\2\2\2\u019a") 207 | buf.write("\u019b\7#\2\2\u019b\u019f\7?\2\2\u019c\u019d\7>\2\2\u019d") 208 | buf.write("\u019f\7@\2\2\u019e\u019a\3\2\2\2\u019e\u019c\3\2\2\2") 209 | buf.write("\u019f@\3\2\2\2\u01a0\u01a1\7>\2\2\u01a1B\3\2\2\2\u01a2") 210 | buf.write("\u01a3\7>\2\2\u01a3\u01a4\7?\2\2\u01a4D\3\2\2\2\u01a5") 211 | buf.write("\u01a6\7@\2\2\u01a6F\3\2\2\2\u01a7\u01a8\7@\2\2\u01a8") 212 | buf.write("\u01a9\7?\2\2\u01a9H\3\2\2\2\u01aa\u01ab\7)\2\2\u01ab") 213 | buf.write("J\3\2\2\2\u01ac\u01ad\7<\2\2\u01adL\3\2\2\2\u01ae\u01af") 214 | buf.write("\7\60\2\2\u01afN\3\2\2\2\u01b0\u01b1\7.\2\2\u01b1P\3\2") 215 | buf.write("\2\2\u01b2\u01b3\7+\2\2\u01b3R\3\2\2\2\u01b4\u01b5\7*") 216 | buf.write("\2\2\u01b5T\3\2\2\2\u01b6\u01b7\7_\2\2\u01b7V\3\2\2\2") 217 | buf.write("\u01b8\u01b9\7]\2\2\u01b9X\3\2\2\2\u01ba\u01bb\7-\2\2") 218 | buf.write("\u01bbZ\3\2\2\2\u01bc\u01bd\5]/\2\u01bd\\\3\2\2\2\u01be") 219 | buf.write("\u01bf\7/\2\2\u01bf^\3\2\2\2\u01c0\u01c1\7`\2\2\u01c1") 220 | buf.write("`\3\2\2\2\u01c2\u01c3\7\61\2\2\u01c3b\3\2\2\2\u01c4\u01c5") 221 | buf.write("\7,\2\2\u01c5d\3\2\2\2\u01c6\u01c7\t\r\2\2\u01c7f\3\2") 222 | buf.write("\2\2\u01c8\u01c9\5e\63\2\u01c9\u01ca\5e\63\2\u01cah\3") 223 | buf.write("\2\2\2\u01cb\u01cc\t\16\2\2\u01ccj\3\2\2\2\u01cd\u01cf") 224 | buf.write("\t\17\2\2\u01ce\u01cd\3\2\2\2\u01cf\u01d0\3\2\2\2\u01d0") 225 | buf.write("\u01ce\3\2\2\2\u01d0\u01d1\3\2\2\2\u01d1\u01d2\3\2\2\2") 226 | buf.write("\u01d2\u01d3\b\66\2\2\u01d3l\3\2\2\2\u01d4\u01d5\7\61") 227 | buf.write("\2\2\u01d5\u01d6\7,\2\2\u01d6\u01da\3\2\2\2\u01d7\u01d9") 228 | buf.write("\13\2\2\2\u01d8\u01d7\3\2\2\2\u01d9\u01dc\3\2\2\2\u01da") 229 | buf.write("\u01db\3\2\2\2\u01da\u01d8\3\2\2\2\u01db\u01dd\3\2\2\2") 230 | buf.write("\u01dc\u01da\3\2\2\2\u01dd\u01de\7,\2\2\u01de\u01df\7") 231 | buf.write("\61\2\2\u01df\u01e0\3\2\2\2\u01e0\u01e1\b\67\2\2\u01e1") 232 | buf.write("n\3\2\2\2\u01e2\u01e3\7\61\2\2\u01e3\u01e4\7\61\2\2\u01e4") 233 | buf.write("\u01e8\3\2\2\2\u01e5\u01e7\n\20\2\2\u01e6\u01e5\3\2\2") 234 | buf.write("\2\u01e7\u01ea\3\2\2\2\u01e8\u01e6\3\2\2\2\u01e8\u01e9") 235 | buf.write("\3\2\2\2\u01e9\u01eb\3\2\2\2\u01ea\u01e8\3\2\2\2\u01eb") 236 | buf.write("\u01ec\b8\2\2\u01ecp\3\2\2\2\u01ed\u01ee\13\2\2\2\u01ee") 237 | buf.write("r\3\2\2\2 \2y|\177\u0086\u0089\u008f\u0096\u0099\u009e") 238 | buf.write("\u00a5\u00ac\u00ba\u00ce\u00d8\u00da\u00e1\u00ee\u00f7") 239 | buf.write("\u00fe\u0108\u010e\u0110\u018b\u0192\u0198\u019e\u01d0") 240 | buf.write("\u01da\u01e8\3\b\2\2") 241 | return buf.getvalue() 242 | 243 | 244 | class STIXPatternLexer(Lexer): 245 | 246 | atn = ATNDeserializer().deserialize(serializedATN()) 247 | 248 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 249 | 250 | IntNegLiteral = 1 251 | IntPosLiteral = 2 252 | FloatNegLiteral = 3 253 | FloatPosLiteral = 4 254 | HexLiteral = 5 255 | BinaryLiteral = 6 256 | StringLiteral = 7 257 | BoolLiteral = 8 258 | TimestampLiteral = 9 259 | AND = 10 260 | OR = 11 261 | NOT = 12 262 | FOLLOWEDBY = 13 263 | LIKE = 14 264 | MATCHES = 15 265 | ISSUPERSET = 16 266 | ISSUBSET = 17 267 | LAST = 18 268 | IN = 19 269 | START = 20 270 | STOP = 21 271 | SECONDS = 22 272 | TRUE = 23 273 | FALSE = 24 274 | WITHIN = 25 275 | REPEATS = 26 276 | TIMES = 27 277 | IdentifierWithoutHyphen = 28 278 | IdentifierWithHyphen = 29 279 | EQ = 30 280 | NEQ = 31 281 | LT = 32 282 | LE = 33 283 | GT = 34 284 | GE = 35 285 | QUOTE = 36 286 | COLON = 37 287 | DOT = 38 288 | COMMA = 39 289 | RPAREN = 40 290 | LPAREN = 41 291 | RBRACK = 42 292 | LBRACK = 43 293 | PLUS = 44 294 | HYPHEN = 45 295 | MINUS = 46 296 | POWER_OP = 47 297 | DIVIDE = 48 298 | ASTERISK = 49 299 | WS = 50 300 | COMMENT = 51 301 | LINE_COMMENT = 52 302 | InvalidCharacter = 53 303 | 304 | channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] 305 | 306 | modeNames = [ "DEFAULT_MODE" ] 307 | 308 | literalNames = [ "", 309 | "'AND'", "'OR'", "'NOT'", "'FOLLOWEDBY'", "'LIKE'", "'MATCHES'", 310 | "'ISSUPERSET'", "'ISSUBSET'", "'LAST'", "'IN'", "'START'", "'STOP'", 311 | "'SECONDS'", "'true'", "'false'", "'WITHIN'", "'REPEATS'", "'TIMES'", 312 | "'<'", "'<='", "'>'", "'>='", "'''", "':'", "'.'", "','", "')'", 313 | "'('", "']'", "'['", "'+'", "'-'", "'^'", "'/'", "'*'" ] 314 | 315 | symbolicNames = [ "", 316 | "IntNegLiteral", "IntPosLiteral", "FloatNegLiteral", "FloatPosLiteral", 317 | "HexLiteral", "BinaryLiteral", "StringLiteral", "BoolLiteral", 318 | "TimestampLiteral", "AND", "OR", "NOT", "FOLLOWEDBY", "LIKE", 319 | "MATCHES", "ISSUPERSET", "ISSUBSET", "LAST", "IN", "START", 320 | "STOP", "SECONDS", "TRUE", "FALSE", "WITHIN", "REPEATS", "TIMES", 321 | "IdentifierWithoutHyphen", "IdentifierWithHyphen", "EQ", "NEQ", 322 | "LT", "LE", "GT", "GE", "QUOTE", "COLON", "DOT", "COMMA", "RPAREN", 323 | "LPAREN", "RBRACK", "LBRACK", "PLUS", "HYPHEN", "MINUS", "POWER_OP", 324 | "DIVIDE", "ASTERISK", "WS", "COMMENT", "LINE_COMMENT", "InvalidCharacter" ] 325 | 326 | ruleNames = [ "IntNegLiteral", "IntPosLiteral", "FloatNegLiteral", "FloatPosLiteral", 327 | "HexLiteral", "BinaryLiteral", "StringLiteral", "BoolLiteral", 328 | "TimestampLiteral", "AND", "OR", "NOT", "FOLLOWEDBY", 329 | "LIKE", "MATCHES", "ISSUPERSET", "ISSUBSET", "LAST", "IN", 330 | "START", "STOP", "SECONDS", "TRUE", "FALSE", "WITHIN", 331 | "REPEATS", "TIMES", "IdentifierWithoutHyphen", "IdentifierWithHyphen", 332 | "EQ", "NEQ", "LT", "LE", "GT", "GE", "QUOTE", "COLON", 333 | "DOT", "COMMA", "RPAREN", "LPAREN", "RBRACK", "LBRACK", 334 | "PLUS", "HYPHEN", "MINUS", "POWER_OP", "DIVIDE", "ASTERISK", 335 | "HexDigit", "TwoHexDigits", "Base64Char", "WS", "COMMENT", 336 | "LINE_COMMENT", "InvalidCharacter" ] 337 | 338 | grammarFileName = "STIXPattern.g4" 339 | 340 | def __init__(self, input=None, output:TextIO = sys.stdout): 341 | super().__init__(input, output) 342 | self.checkVersion("4.9.2") 343 | self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) 344 | self._actions = None 345 | self._predicates = None 346 | 347 | 348 | -------------------------------------------------------------------------------- /stix2patterns/v20/grammars/STIXPatternListener.py: -------------------------------------------------------------------------------- 1 | # Generated from STIXPattern.g4 by ANTLR 4.9.2 2 | from antlr4 import * 3 | if __name__ is not None and "." in __name__: 4 | from .STIXPatternParser import STIXPatternParser 5 | else: 6 | from STIXPatternParser import STIXPatternParser 7 | 8 | # This class defines a complete listener for a parse tree produced by STIXPatternParser. 9 | class STIXPatternListener(ParseTreeListener): 10 | 11 | # Enter a parse tree produced by STIXPatternParser#pattern. 12 | def enterPattern(self, ctx:STIXPatternParser.PatternContext): 13 | pass 14 | 15 | # Exit a parse tree produced by STIXPatternParser#pattern. 16 | def exitPattern(self, ctx:STIXPatternParser.PatternContext): 17 | pass 18 | 19 | 20 | # Enter a parse tree produced by STIXPatternParser#observationExpressions. 21 | def enterObservationExpressions(self, ctx:STIXPatternParser.ObservationExpressionsContext): 22 | pass 23 | 24 | # Exit a parse tree produced by STIXPatternParser#observationExpressions. 25 | def exitObservationExpressions(self, ctx:STIXPatternParser.ObservationExpressionsContext): 26 | pass 27 | 28 | 29 | # Enter a parse tree produced by STIXPatternParser#observationExpressionOr. 30 | def enterObservationExpressionOr(self, ctx:STIXPatternParser.ObservationExpressionOrContext): 31 | pass 32 | 33 | # Exit a parse tree produced by STIXPatternParser#observationExpressionOr. 34 | def exitObservationExpressionOr(self, ctx:STIXPatternParser.ObservationExpressionOrContext): 35 | pass 36 | 37 | 38 | # Enter a parse tree produced by STIXPatternParser#observationExpressionAnd. 39 | def enterObservationExpressionAnd(self, ctx:STIXPatternParser.ObservationExpressionAndContext): 40 | pass 41 | 42 | # Exit a parse tree produced by STIXPatternParser#observationExpressionAnd. 43 | def exitObservationExpressionAnd(self, ctx:STIXPatternParser.ObservationExpressionAndContext): 44 | pass 45 | 46 | 47 | # Enter a parse tree produced by STIXPatternParser#observationExpressionRepeated. 48 | def enterObservationExpressionRepeated(self, ctx:STIXPatternParser.ObservationExpressionRepeatedContext): 49 | pass 50 | 51 | # Exit a parse tree produced by STIXPatternParser#observationExpressionRepeated. 52 | def exitObservationExpressionRepeated(self, ctx:STIXPatternParser.ObservationExpressionRepeatedContext): 53 | pass 54 | 55 | 56 | # Enter a parse tree produced by STIXPatternParser#observationExpressionSimple. 57 | def enterObservationExpressionSimple(self, ctx:STIXPatternParser.ObservationExpressionSimpleContext): 58 | pass 59 | 60 | # Exit a parse tree produced by STIXPatternParser#observationExpressionSimple. 61 | def exitObservationExpressionSimple(self, ctx:STIXPatternParser.ObservationExpressionSimpleContext): 62 | pass 63 | 64 | 65 | # Enter a parse tree produced by STIXPatternParser#observationExpressionCompound. 66 | def enterObservationExpressionCompound(self, ctx:STIXPatternParser.ObservationExpressionCompoundContext): 67 | pass 68 | 69 | # Exit a parse tree produced by STIXPatternParser#observationExpressionCompound. 70 | def exitObservationExpressionCompound(self, ctx:STIXPatternParser.ObservationExpressionCompoundContext): 71 | pass 72 | 73 | 74 | # Enter a parse tree produced by STIXPatternParser#observationExpressionWithin. 75 | def enterObservationExpressionWithin(self, ctx:STIXPatternParser.ObservationExpressionWithinContext): 76 | pass 77 | 78 | # Exit a parse tree produced by STIXPatternParser#observationExpressionWithin. 79 | def exitObservationExpressionWithin(self, ctx:STIXPatternParser.ObservationExpressionWithinContext): 80 | pass 81 | 82 | 83 | # Enter a parse tree produced by STIXPatternParser#observationExpressionStartStop. 84 | def enterObservationExpressionStartStop(self, ctx:STIXPatternParser.ObservationExpressionStartStopContext): 85 | pass 86 | 87 | # Exit a parse tree produced by STIXPatternParser#observationExpressionStartStop. 88 | def exitObservationExpressionStartStop(self, ctx:STIXPatternParser.ObservationExpressionStartStopContext): 89 | pass 90 | 91 | 92 | # Enter a parse tree produced by STIXPatternParser#comparisonExpression. 93 | def enterComparisonExpression(self, ctx:STIXPatternParser.ComparisonExpressionContext): 94 | pass 95 | 96 | # Exit a parse tree produced by STIXPatternParser#comparisonExpression. 97 | def exitComparisonExpression(self, ctx:STIXPatternParser.ComparisonExpressionContext): 98 | pass 99 | 100 | 101 | # Enter a parse tree produced by STIXPatternParser#comparisonExpressionAnd. 102 | def enterComparisonExpressionAnd(self, ctx:STIXPatternParser.ComparisonExpressionAndContext): 103 | pass 104 | 105 | # Exit a parse tree produced by STIXPatternParser#comparisonExpressionAnd. 106 | def exitComparisonExpressionAnd(self, ctx:STIXPatternParser.ComparisonExpressionAndContext): 107 | pass 108 | 109 | 110 | # Enter a parse tree produced by STIXPatternParser#propTestEqual. 111 | def enterPropTestEqual(self, ctx:STIXPatternParser.PropTestEqualContext): 112 | pass 113 | 114 | # Exit a parse tree produced by STIXPatternParser#propTestEqual. 115 | def exitPropTestEqual(self, ctx:STIXPatternParser.PropTestEqualContext): 116 | pass 117 | 118 | 119 | # Enter a parse tree produced by STIXPatternParser#propTestOrder. 120 | def enterPropTestOrder(self, ctx:STIXPatternParser.PropTestOrderContext): 121 | pass 122 | 123 | # Exit a parse tree produced by STIXPatternParser#propTestOrder. 124 | def exitPropTestOrder(self, ctx:STIXPatternParser.PropTestOrderContext): 125 | pass 126 | 127 | 128 | # Enter a parse tree produced by STIXPatternParser#propTestSet. 129 | def enterPropTestSet(self, ctx:STIXPatternParser.PropTestSetContext): 130 | pass 131 | 132 | # Exit a parse tree produced by STIXPatternParser#propTestSet. 133 | def exitPropTestSet(self, ctx:STIXPatternParser.PropTestSetContext): 134 | pass 135 | 136 | 137 | # Enter a parse tree produced by STIXPatternParser#propTestLike. 138 | def enterPropTestLike(self, ctx:STIXPatternParser.PropTestLikeContext): 139 | pass 140 | 141 | # Exit a parse tree produced by STIXPatternParser#propTestLike. 142 | def exitPropTestLike(self, ctx:STIXPatternParser.PropTestLikeContext): 143 | pass 144 | 145 | 146 | # Enter a parse tree produced by STIXPatternParser#propTestRegex. 147 | def enterPropTestRegex(self, ctx:STIXPatternParser.PropTestRegexContext): 148 | pass 149 | 150 | # Exit a parse tree produced by STIXPatternParser#propTestRegex. 151 | def exitPropTestRegex(self, ctx:STIXPatternParser.PropTestRegexContext): 152 | pass 153 | 154 | 155 | # Enter a parse tree produced by STIXPatternParser#propTestIsSubset. 156 | def enterPropTestIsSubset(self, ctx:STIXPatternParser.PropTestIsSubsetContext): 157 | pass 158 | 159 | # Exit a parse tree produced by STIXPatternParser#propTestIsSubset. 160 | def exitPropTestIsSubset(self, ctx:STIXPatternParser.PropTestIsSubsetContext): 161 | pass 162 | 163 | 164 | # Enter a parse tree produced by STIXPatternParser#propTestIsSuperset. 165 | def enterPropTestIsSuperset(self, ctx:STIXPatternParser.PropTestIsSupersetContext): 166 | pass 167 | 168 | # Exit a parse tree produced by STIXPatternParser#propTestIsSuperset. 169 | def exitPropTestIsSuperset(self, ctx:STIXPatternParser.PropTestIsSupersetContext): 170 | pass 171 | 172 | 173 | # Enter a parse tree produced by STIXPatternParser#propTestParen. 174 | def enterPropTestParen(self, ctx:STIXPatternParser.PropTestParenContext): 175 | pass 176 | 177 | # Exit a parse tree produced by STIXPatternParser#propTestParen. 178 | def exitPropTestParen(self, ctx:STIXPatternParser.PropTestParenContext): 179 | pass 180 | 181 | 182 | # Enter a parse tree produced by STIXPatternParser#startStopQualifier. 183 | def enterStartStopQualifier(self, ctx:STIXPatternParser.StartStopQualifierContext): 184 | pass 185 | 186 | # Exit a parse tree produced by STIXPatternParser#startStopQualifier. 187 | def exitStartStopQualifier(self, ctx:STIXPatternParser.StartStopQualifierContext): 188 | pass 189 | 190 | 191 | # Enter a parse tree produced by STIXPatternParser#withinQualifier. 192 | def enterWithinQualifier(self, ctx:STIXPatternParser.WithinQualifierContext): 193 | pass 194 | 195 | # Exit a parse tree produced by STIXPatternParser#withinQualifier. 196 | def exitWithinQualifier(self, ctx:STIXPatternParser.WithinQualifierContext): 197 | pass 198 | 199 | 200 | # Enter a parse tree produced by STIXPatternParser#repeatedQualifier. 201 | def enterRepeatedQualifier(self, ctx:STIXPatternParser.RepeatedQualifierContext): 202 | pass 203 | 204 | # Exit a parse tree produced by STIXPatternParser#repeatedQualifier. 205 | def exitRepeatedQualifier(self, ctx:STIXPatternParser.RepeatedQualifierContext): 206 | pass 207 | 208 | 209 | # Enter a parse tree produced by STIXPatternParser#objectPath. 210 | def enterObjectPath(self, ctx:STIXPatternParser.ObjectPathContext): 211 | pass 212 | 213 | # Exit a parse tree produced by STIXPatternParser#objectPath. 214 | def exitObjectPath(self, ctx:STIXPatternParser.ObjectPathContext): 215 | pass 216 | 217 | 218 | # Enter a parse tree produced by STIXPatternParser#objectType. 219 | def enterObjectType(self, ctx:STIXPatternParser.ObjectTypeContext): 220 | pass 221 | 222 | # Exit a parse tree produced by STIXPatternParser#objectType. 223 | def exitObjectType(self, ctx:STIXPatternParser.ObjectTypeContext): 224 | pass 225 | 226 | 227 | # Enter a parse tree produced by STIXPatternParser#firstPathComponent. 228 | def enterFirstPathComponent(self, ctx:STIXPatternParser.FirstPathComponentContext): 229 | pass 230 | 231 | # Exit a parse tree produced by STIXPatternParser#firstPathComponent. 232 | def exitFirstPathComponent(self, ctx:STIXPatternParser.FirstPathComponentContext): 233 | pass 234 | 235 | 236 | # Enter a parse tree produced by STIXPatternParser#indexPathStep. 237 | def enterIndexPathStep(self, ctx:STIXPatternParser.IndexPathStepContext): 238 | pass 239 | 240 | # Exit a parse tree produced by STIXPatternParser#indexPathStep. 241 | def exitIndexPathStep(self, ctx:STIXPatternParser.IndexPathStepContext): 242 | pass 243 | 244 | 245 | # Enter a parse tree produced by STIXPatternParser#pathStep. 246 | def enterPathStep(self, ctx:STIXPatternParser.PathStepContext): 247 | pass 248 | 249 | # Exit a parse tree produced by STIXPatternParser#pathStep. 250 | def exitPathStep(self, ctx:STIXPatternParser.PathStepContext): 251 | pass 252 | 253 | 254 | # Enter a parse tree produced by STIXPatternParser#keyPathStep. 255 | def enterKeyPathStep(self, ctx:STIXPatternParser.KeyPathStepContext): 256 | pass 257 | 258 | # Exit a parse tree produced by STIXPatternParser#keyPathStep. 259 | def exitKeyPathStep(self, ctx:STIXPatternParser.KeyPathStepContext): 260 | pass 261 | 262 | 263 | # Enter a parse tree produced by STIXPatternParser#setLiteral. 264 | def enterSetLiteral(self, ctx:STIXPatternParser.SetLiteralContext): 265 | pass 266 | 267 | # Exit a parse tree produced by STIXPatternParser#setLiteral. 268 | def exitSetLiteral(self, ctx:STIXPatternParser.SetLiteralContext): 269 | pass 270 | 271 | 272 | # Enter a parse tree produced by STIXPatternParser#primitiveLiteral. 273 | def enterPrimitiveLiteral(self, ctx:STIXPatternParser.PrimitiveLiteralContext): 274 | pass 275 | 276 | # Exit a parse tree produced by STIXPatternParser#primitiveLiteral. 277 | def exitPrimitiveLiteral(self, ctx:STIXPatternParser.PrimitiveLiteralContext): 278 | pass 279 | 280 | 281 | # Enter a parse tree produced by STIXPatternParser#orderableLiteral. 282 | def enterOrderableLiteral(self, ctx:STIXPatternParser.OrderableLiteralContext): 283 | pass 284 | 285 | # Exit a parse tree produced by STIXPatternParser#orderableLiteral. 286 | def exitOrderableLiteral(self, ctx:STIXPatternParser.OrderableLiteralContext): 287 | pass 288 | 289 | 290 | 291 | del STIXPatternParser -------------------------------------------------------------------------------- /stix2patterns/v20/grammars/STIXPatternVisitor.py: -------------------------------------------------------------------------------- 1 | # Generated from STIXPattern.g4 by ANTLR 4.9.2 2 | from antlr4 import * 3 | if __name__ is not None and "." in __name__: 4 | from .STIXPatternParser import STIXPatternParser 5 | else: 6 | from STIXPatternParser import STIXPatternParser 7 | 8 | # This class defines a complete generic visitor for a parse tree produced by STIXPatternParser. 9 | 10 | class STIXPatternVisitor(ParseTreeVisitor): 11 | 12 | # Visit a parse tree produced by STIXPatternParser#pattern. 13 | def visitPattern(self, ctx:STIXPatternParser.PatternContext): 14 | return self.visitChildren(ctx) 15 | 16 | 17 | # Visit a parse tree produced by STIXPatternParser#observationExpressions. 18 | def visitObservationExpressions(self, ctx:STIXPatternParser.ObservationExpressionsContext): 19 | return self.visitChildren(ctx) 20 | 21 | 22 | # Visit a parse tree produced by STIXPatternParser#observationExpressionOr. 23 | def visitObservationExpressionOr(self, ctx:STIXPatternParser.ObservationExpressionOrContext): 24 | return self.visitChildren(ctx) 25 | 26 | 27 | # Visit a parse tree produced by STIXPatternParser#observationExpressionAnd. 28 | def visitObservationExpressionAnd(self, ctx:STIXPatternParser.ObservationExpressionAndContext): 29 | return self.visitChildren(ctx) 30 | 31 | 32 | # Visit a parse tree produced by STIXPatternParser#observationExpressionRepeated. 33 | def visitObservationExpressionRepeated(self, ctx:STIXPatternParser.ObservationExpressionRepeatedContext): 34 | return self.visitChildren(ctx) 35 | 36 | 37 | # Visit a parse tree produced by STIXPatternParser#observationExpressionSimple. 38 | def visitObservationExpressionSimple(self, ctx:STIXPatternParser.ObservationExpressionSimpleContext): 39 | return self.visitChildren(ctx) 40 | 41 | 42 | # Visit a parse tree produced by STIXPatternParser#observationExpressionCompound. 43 | def visitObservationExpressionCompound(self, ctx:STIXPatternParser.ObservationExpressionCompoundContext): 44 | return self.visitChildren(ctx) 45 | 46 | 47 | # Visit a parse tree produced by STIXPatternParser#observationExpressionWithin. 48 | def visitObservationExpressionWithin(self, ctx:STIXPatternParser.ObservationExpressionWithinContext): 49 | return self.visitChildren(ctx) 50 | 51 | 52 | # Visit a parse tree produced by STIXPatternParser#observationExpressionStartStop. 53 | def visitObservationExpressionStartStop(self, ctx:STIXPatternParser.ObservationExpressionStartStopContext): 54 | return self.visitChildren(ctx) 55 | 56 | 57 | # Visit a parse tree produced by STIXPatternParser#comparisonExpression. 58 | def visitComparisonExpression(self, ctx:STIXPatternParser.ComparisonExpressionContext): 59 | return self.visitChildren(ctx) 60 | 61 | 62 | # Visit a parse tree produced by STIXPatternParser#comparisonExpressionAnd. 63 | def visitComparisonExpressionAnd(self, ctx:STIXPatternParser.ComparisonExpressionAndContext): 64 | return self.visitChildren(ctx) 65 | 66 | 67 | # Visit a parse tree produced by STIXPatternParser#propTestEqual. 68 | def visitPropTestEqual(self, ctx:STIXPatternParser.PropTestEqualContext): 69 | return self.visitChildren(ctx) 70 | 71 | 72 | # Visit a parse tree produced by STIXPatternParser#propTestOrder. 73 | def visitPropTestOrder(self, ctx:STIXPatternParser.PropTestOrderContext): 74 | return self.visitChildren(ctx) 75 | 76 | 77 | # Visit a parse tree produced by STIXPatternParser#propTestSet. 78 | def visitPropTestSet(self, ctx:STIXPatternParser.PropTestSetContext): 79 | return self.visitChildren(ctx) 80 | 81 | 82 | # Visit a parse tree produced by STIXPatternParser#propTestLike. 83 | def visitPropTestLike(self, ctx:STIXPatternParser.PropTestLikeContext): 84 | return self.visitChildren(ctx) 85 | 86 | 87 | # Visit a parse tree produced by STIXPatternParser#propTestRegex. 88 | def visitPropTestRegex(self, ctx:STIXPatternParser.PropTestRegexContext): 89 | return self.visitChildren(ctx) 90 | 91 | 92 | # Visit a parse tree produced by STIXPatternParser#propTestIsSubset. 93 | def visitPropTestIsSubset(self, ctx:STIXPatternParser.PropTestIsSubsetContext): 94 | return self.visitChildren(ctx) 95 | 96 | 97 | # Visit a parse tree produced by STIXPatternParser#propTestIsSuperset. 98 | def visitPropTestIsSuperset(self, ctx:STIXPatternParser.PropTestIsSupersetContext): 99 | return self.visitChildren(ctx) 100 | 101 | 102 | # Visit a parse tree produced by STIXPatternParser#propTestParen. 103 | def visitPropTestParen(self, ctx:STIXPatternParser.PropTestParenContext): 104 | return self.visitChildren(ctx) 105 | 106 | 107 | # Visit a parse tree produced by STIXPatternParser#startStopQualifier. 108 | def visitStartStopQualifier(self, ctx:STIXPatternParser.StartStopQualifierContext): 109 | return self.visitChildren(ctx) 110 | 111 | 112 | # Visit a parse tree produced by STIXPatternParser#withinQualifier. 113 | def visitWithinQualifier(self, ctx:STIXPatternParser.WithinQualifierContext): 114 | return self.visitChildren(ctx) 115 | 116 | 117 | # Visit a parse tree produced by STIXPatternParser#repeatedQualifier. 118 | def visitRepeatedQualifier(self, ctx:STIXPatternParser.RepeatedQualifierContext): 119 | return self.visitChildren(ctx) 120 | 121 | 122 | # Visit a parse tree produced by STIXPatternParser#objectPath. 123 | def visitObjectPath(self, ctx:STIXPatternParser.ObjectPathContext): 124 | return self.visitChildren(ctx) 125 | 126 | 127 | # Visit a parse tree produced by STIXPatternParser#objectType. 128 | def visitObjectType(self, ctx:STIXPatternParser.ObjectTypeContext): 129 | return self.visitChildren(ctx) 130 | 131 | 132 | # Visit a parse tree produced by STIXPatternParser#firstPathComponent. 133 | def visitFirstPathComponent(self, ctx:STIXPatternParser.FirstPathComponentContext): 134 | return self.visitChildren(ctx) 135 | 136 | 137 | # Visit a parse tree produced by STIXPatternParser#indexPathStep. 138 | def visitIndexPathStep(self, ctx:STIXPatternParser.IndexPathStepContext): 139 | return self.visitChildren(ctx) 140 | 141 | 142 | # Visit a parse tree produced by STIXPatternParser#pathStep. 143 | def visitPathStep(self, ctx:STIXPatternParser.PathStepContext): 144 | return self.visitChildren(ctx) 145 | 146 | 147 | # Visit a parse tree produced by STIXPatternParser#keyPathStep. 148 | def visitKeyPathStep(self, ctx:STIXPatternParser.KeyPathStepContext): 149 | return self.visitChildren(ctx) 150 | 151 | 152 | # Visit a parse tree produced by STIXPatternParser#setLiteral. 153 | def visitSetLiteral(self, ctx:STIXPatternParser.SetLiteralContext): 154 | return self.visitChildren(ctx) 155 | 156 | 157 | # Visit a parse tree produced by STIXPatternParser#primitiveLiteral. 158 | def visitPrimitiveLiteral(self, ctx:STIXPatternParser.PrimitiveLiteralContext): 159 | return self.visitChildren(ctx) 160 | 161 | 162 | # Visit a parse tree produced by STIXPatternParser#orderableLiteral. 163 | def visitOrderableLiteral(self, ctx:STIXPatternParser.OrderableLiteralContext): 164 | return self.visitChildren(ctx) 165 | 166 | 167 | 168 | del STIXPatternParser -------------------------------------------------------------------------------- /stix2patterns/v20/grammars/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/v20/grammars/__init__.py -------------------------------------------------------------------------------- /stix2patterns/v20/inspector.py: -------------------------------------------------------------------------------- 1 | from ..inspector import INDEX_STAR, _PatternData, _string_literal_to_string 2 | from .grammars.STIXPatternListener import STIXPatternListener 3 | 4 | 5 | class InspectionListener(STIXPatternListener): 6 | """This listener collects info about a pattern and puts it 7 | in a python structure. It is intended to assist apps which wish to 8 | look "inside" a pattern and know what's in there. 9 | """ 10 | 11 | def __init__(self): 12 | self.__comparison_data = {} 13 | self.__qualifiers = set() 14 | self.__observation_ops = set() 15 | self.__obj_type = None 16 | self.__obj_path = None 17 | 18 | def pattern_data(self): 19 | return _PatternData(self.__comparison_data, self.__observation_ops, 20 | self.__qualifiers) 21 | 22 | def __add_prop_tuple(self, obj_type, obj_path, op, value): 23 | if obj_type not in self.__comparison_data: 24 | self.__comparison_data[obj_type] = [] 25 | 26 | self.__comparison_data[obj_type].append((obj_path, op, value)) 27 | 28 | def exitObservationExpressions(self, ctx): 29 | if ctx.FOLLOWEDBY(): 30 | self.__observation_ops.add(u"FOLLOWEDBY") 31 | 32 | def exitObservationExpressionOr(self, ctx): 33 | if ctx.OR(): 34 | self.__observation_ops.add(u"OR") 35 | 36 | def exitObservationExpressionAnd(self, ctx): 37 | if ctx.AND(): 38 | self.__observation_ops.add(u"AND") 39 | 40 | def exitStartStopQualifier(self, ctx): 41 | self.__qualifiers.add( 42 | u"START {0} STOP {1}".format( 43 | ctx.StringLiteral(0), ctx.StringLiteral(1) 44 | ) 45 | ) 46 | 47 | def exitWithinQualifier(self, ctx): 48 | self.__qualifiers.add( 49 | u"WITHIN {0} SECONDS".format( 50 | ctx.IntPosLiteral() or ctx.FloatPosLiteral() 51 | ) 52 | ) 53 | 54 | def exitRepeatedQualifier(self, ctx): 55 | self.__qualifiers.add( 56 | u"REPEATS {0} TIMES".format( 57 | ctx.IntPosLiteral() 58 | ) 59 | ) 60 | 61 | def exitPropTestEqual(self, ctx): 62 | op_tok = ctx.EQ() or ctx.NEQ() 63 | op_str = u"NOT " if ctx.NOT() else u"" 64 | op_str += op_tok.getText() 65 | 66 | value = ctx.primitiveLiteral().getText() 67 | 68 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 69 | value) 70 | 71 | def exitPropTestOrder(self, ctx): 72 | op_tok = ctx.GT() or ctx.LT() or ctx.GE() or ctx.LE() 73 | op_str = u"NOT " if ctx.NOT() else u"" 74 | op_str += op_tok.getText() 75 | 76 | value = ctx.orderableLiteral().getText() 77 | 78 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 79 | value) 80 | 81 | def exitPropTestSet(self, ctx): 82 | op_str = u"NOT " if ctx.NOT() else u"" 83 | op_str += u"IN" 84 | 85 | value = ctx.setLiteral().getText() 86 | 87 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 88 | value) 89 | 90 | def exitPropTestLike(self, ctx): 91 | op_str = u"NOT " if ctx.NOT() else u"" 92 | op_str += u"LIKE" 93 | 94 | value = ctx.StringLiteral().getText() 95 | 96 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 97 | value) 98 | 99 | def exitPropTestRegex(self, ctx): 100 | op_str = u"NOT " if ctx.NOT() else u"" 101 | op_str += u"MATCHES" 102 | 103 | value = ctx.StringLiteral().getText() 104 | 105 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 106 | value) 107 | 108 | def exitPropTestIsSubset(self, ctx): 109 | op_str = u"NOT " if ctx.NOT() else u"" 110 | op_str += u"ISSUBSET" 111 | 112 | value = ctx.StringLiteral().getText() 113 | 114 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 115 | value) 116 | 117 | def exitPropTestIsSuperset(self, ctx): 118 | op_str = u"NOT " if ctx.NOT() else u"" 119 | op_str += u"ISSUPERSET" 120 | 121 | value = ctx.StringLiteral().getText() 122 | 123 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 124 | value) 125 | 126 | def exitObjectType(self, ctx): 127 | self.__obj_type = ctx.getText() 128 | 129 | def exitFirstPathComponent(self, ctx): 130 | if ctx.StringLiteral(): 131 | path_component = _string_literal_to_string(ctx.StringLiteral()) 132 | else: 133 | path_component = ctx.getText() 134 | 135 | self.__obj_path = [path_component] 136 | 137 | def exitKeyPathStep(self, ctx): 138 | if ctx.IdentifierWithoutHyphen(): 139 | path_component = ctx.IdentifierWithoutHyphen().getText() 140 | else: # A StringLiteral 141 | path_component = _string_literal_to_string(ctx.StringLiteral()) 142 | 143 | self.__obj_path.append(path_component) 144 | 145 | def exitIndexPathStep(self, ctx): 146 | if ctx.ASTERISK(): 147 | self.__obj_path.append(INDEX_STAR) 148 | else: 149 | self.__obj_path.append(int(ctx.IntPosLiteral().getText())) 150 | -------------------------------------------------------------------------------- /stix2patterns/v20/object_validator.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import stix2patterns.inspector 4 | 5 | HASHES_REGEX = { 6 | "MD5": (r"^[a-fA-F0-9]{32}$", "MD5"), 7 | "MD6": (r"^[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{56}|\ 8 | [a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}$", "MD6"), 9 | "RIPEMD160": (r"^[a-fA-F0-9]{40}$", "RIPEMD-160"), 10 | "SHA1": (r"^[a-fA-F0-9]{40}$", "SHA-1"), 11 | "SHA224": (r"^[a-fA-F0-9]{56}$", "SHA-224"), 12 | "SHA256": (r"^[a-fA-F0-9]{64}$", "SHA-256"), 13 | "SHA384": (r"^[a-fA-F0-9]{96}$", "SHA-384"), 14 | "SHA512": (r"^[a-fA-F0-9]{128}$", "SHA-512"), 15 | "SHA3224": (r"^[a-fA-F0-9]{56}$", "SHA3-224"), 16 | "SHA3256": (r"^[a-fA-F0-9]{64}$", "SHA3-256"), 17 | "SHA3384": (r"^[a-fA-F0-9]{96}$", "SHA3-384"), 18 | "SHA3512": (r"^[a-fA-F0-9]{128}$", "SHA3-512"), 19 | "SSDEEP": (r"^[a-zA-Z0-9/+:.]{1,128}$", "ssdeep"), 20 | "WHIRLPOOL": (r"^[a-fA-F0-9]{128}$", "WHIRLPOOL"), 21 | } 22 | 23 | 24 | def verify_object(patt_data): 25 | error_list = [] 26 | msg = "FAIL: '{}' is not a valid {} hash" 27 | 28 | # iterate over observed objects 29 | for type_name, comp in patt_data.comparisons.items(): 30 | for obj_path, op, value in comp: 31 | if 'hashes' in obj_path: 32 | hash_selector = obj_path[-1] 33 | if hash_selector is not stix2patterns.inspector.INDEX_STAR: 34 | hash_type = \ 35 | hash_selector.upper().replace('-', '').replace("'", "") 36 | hash_string = value.replace("'", "") 37 | if hash_type in HASHES_REGEX: 38 | if not re.match(HASHES_REGEX[hash_type][0], hash_string): 39 | error_list.append( 40 | msg.format(hash_string, hash_selector) 41 | ) 42 | return error_list 43 | -------------------------------------------------------------------------------- /stix2patterns/v20/pattern.py: -------------------------------------------------------------------------------- 1 | import antlr4 2 | import six 3 | 4 | from ..exceptions import ParseException, ParserErrorListener 5 | from .grammars.STIXPatternLexer import STIXPatternLexer 6 | from .grammars.STIXPatternParser import STIXPatternParser 7 | from .inspector import InspectionListener 8 | 9 | 10 | class Pattern(object): 11 | """ 12 | Represents a pattern in a "compiled" form, for more efficient reuse. 13 | """ 14 | def __init__(self, pattern_str): 15 | """ 16 | Compile a pattern. 17 | 18 | :param pattern_str: The pattern to compile 19 | :raises ParseException: If there is a parse error 20 | """ 21 | self.__parse_tree = self.__do_parse(pattern_str) 22 | 23 | def inspect(self): 24 | """ 25 | Inspect a pattern. This gives information regarding the sorts of 26 | operations, content, etc in use in the pattern. 27 | 28 | :return: Pattern information 29 | """ 30 | 31 | inspector = InspectionListener() 32 | self.walk(inspector) 33 | 34 | return inspector.pattern_data() 35 | 36 | def walk(self, listener): 37 | """Walk the parse tree, using the given listener. The listener 38 | should be a 39 | stix2patterns.grammars.STIXPatternListener.STIXPatternListener (or 40 | subclass) instance.""" 41 | antlr4.ParseTreeWalker.DEFAULT.walk(listener, self.__parse_tree) 42 | 43 | def visit(self, visitor): 44 | """ 45 | Walk the parse tree using the given visitor. 46 | 47 | :param visitor: A visitor object (STIXPatternVisitor instance) 48 | :return: The visitor's return value 49 | """ 50 | return visitor.visit(self.__parse_tree) 51 | 52 | def __do_parse(self, pattern_str): 53 | """ 54 | Parses the given pattern and returns the antlr parse tree. 55 | 56 | :param pattern_str: The STIX pattern 57 | :return: The parse tree 58 | :raises ParseException: If there is a parse error 59 | """ 60 | in_ = antlr4.InputStream(pattern_str) 61 | lexer = STIXPatternLexer(in_) 62 | lexer.removeErrorListeners() # remove the default "console" listener 63 | token_stream = antlr4.CommonTokenStream(lexer) 64 | 65 | parser = STIXPatternParser(token_stream) 66 | parser.removeErrorListeners() # remove the default "console" listener 67 | error_listener = ParserErrorListener() 68 | parser.addErrorListener(error_listener) 69 | 70 | # I found no public API for this... 71 | # The default error handler tries to keep parsing, and I don't 72 | # think that's appropriate here. (These error handlers are only for 73 | # handling the built-in RecognitionException errors.) 74 | parser._errHandler = antlr4.BailErrorStrategy() 75 | 76 | # To improve error messages, replace "" in the literal 77 | # names with symbolic names. This is a hack, but seemed like 78 | # the simplest workaround. 79 | for i, lit_name in enumerate(parser.literalNames): 80 | if lit_name == u"": 81 | parser.literalNames[i] = parser.symbolicNames[i] 82 | 83 | # parser.setTrace(True) 84 | 85 | try: 86 | tree = parser.pattern() 87 | # print(tree.toStringTree(recog=parser)) 88 | 89 | return tree 90 | except antlr4.error.Errors.ParseCancellationException as e: 91 | # The cancellation exception wraps the real RecognitionException 92 | # which caused the parser to bail. 93 | real_exc = e.args[0] 94 | 95 | # I want to bail when the first error is hit. But I also want 96 | # a decent error message. When an error is encountered in 97 | # Parser.match(), the BailErrorStrategy produces the 98 | # ParseCancellationException. It is not a subclass of 99 | # RecognitionException, so none of the 'except' clauses which would 100 | # normally report an error are invoked. 101 | # 102 | # Error message creation is buried in the ErrorStrategy, and I can 103 | # (ab)use the API to get a message: register an error listener with 104 | # the parser, force an error report, then get the message out of the 105 | # listener. Error listener registration is above; now we force its 106 | # invocation. Wish this could be cleaner... 107 | parser._errHandler.reportError(parser, real_exc) 108 | 109 | # should probably chain exceptions if we can... 110 | # Should I report the cancellation or recognition exception as the 111 | # cause...? 112 | six.raise_from(ParseException(error_listener.error_message), 113 | real_exc) 114 | -------------------------------------------------------------------------------- /stix2patterns/v20/validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Validates a user entered pattern against STIXPattern grammar. 3 | """ 4 | 5 | from antlr4 import CommonTokenStream, ParseTreeWalker 6 | 7 | from . import object_validator 8 | from ..exceptions import STIXPatternErrorListener 9 | from .grammars.STIXPatternLexer import STIXPatternLexer 10 | from .grammars.STIXPatternParser import STIXPatternParser 11 | from .inspector import InspectionListener 12 | 13 | 14 | def run_validator(pattern): 15 | """ 16 | Validates a pattern against the STIX Pattern grammar. Error messages are 17 | returned in a list. The test passed if the returned list is empty. 18 | """ 19 | parseErrListener = STIXPatternErrorListener() 20 | 21 | lexer = STIXPatternLexer(pattern) 22 | # it always adds a console listener by default... remove it. 23 | lexer.removeErrorListeners() 24 | 25 | stream = CommonTokenStream(lexer) 26 | 27 | parser = STIXPatternParser(stream) 28 | 29 | # it always adds a console listener by default... remove it. 30 | parser.removeErrorListeners() 31 | parser.addErrorListener(parseErrListener) 32 | 33 | # To improve error messages, replace "" in the literal 34 | # names with symbolic names. This is a hack, but seemed like 35 | # the simplest workaround. 36 | for i, lit_name in enumerate(parser.literalNames): 37 | if lit_name == u"": 38 | parser.literalNames[i] = parser.symbolicNames[i] 39 | 40 | tree = parser.pattern() 41 | inspection_listener = InspectionListener() 42 | 43 | # validate observed objects 44 | if len(parseErrListener.err_strings) == 0: 45 | ParseTreeWalker.DEFAULT.walk(inspection_listener, tree) 46 | patt_data = inspection_listener.pattern_data() 47 | obj_validator_results = object_validator.verify_object(patt_data) 48 | 49 | if obj_validator_results: 50 | parseErrListener.err_strings.extend(obj_validator_results) 51 | 52 | return parseErrListener.err_strings 53 | -------------------------------------------------------------------------------- /stix2patterns/v21/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/v21/__init__.py -------------------------------------------------------------------------------- /stix2patterns/v21/grammars/STIXPatternLexer.py: -------------------------------------------------------------------------------- 1 | # Generated from STIXPattern.g4 by ANTLR 4.9.2 2 | from antlr4 import * 3 | from io import StringIO 4 | import sys 5 | if sys.version_info[1] > 5: 6 | from typing import TextIO 7 | else: 8 | from typing.io import TextIO 9 | 10 | 11 | 12 | def serializedATN(): 13 | with StringIO() as buf: 14 | buf.write("\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\28") 15 | buf.write("\u01f8\b\1\4\2\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7") 16 | buf.write("\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13\t\13\4\f\t\f\4\r\t\r") 17 | buf.write("\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22\4\23") 18 | buf.write("\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30") 19 | buf.write("\4\31\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36") 20 | buf.write("\t\36\4\37\t\37\4 \t \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%") 21 | buf.write("\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t+\4,\t,\4-\t-\4.") 22 | buf.write("\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64") 23 | buf.write("\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:") 24 | buf.write("\3\2\3\2\3\2\3\2\7\2z\n\2\f\2\16\2}\13\2\5\2\177\n\2\3") 25 | buf.write("\3\5\3\u0082\n\3\3\3\3\3\3\3\7\3\u0087\n\3\f\3\16\3\u008a") 26 | buf.write("\13\3\5\3\u008c\n\3\3\4\3\4\7\4\u0090\n\4\f\4\16\4\u0093") 27 | buf.write("\13\4\3\4\3\4\6\4\u0097\n\4\r\4\16\4\u0098\3\5\5\5\u009c") 28 | buf.write("\n\5\3\5\7\5\u009f\n\5\f\5\16\5\u00a2\13\5\3\5\3\5\6\5") 29 | buf.write("\u00a6\n\5\r\5\16\5\u00a7\3\6\3\6\3\6\7\6\u00ad\n\6\f") 30 | buf.write("\6\16\6\u00b0\13\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7") 31 | buf.write("\7\7\u00bb\n\7\f\7\16\7\u00be\13\7\3\7\3\7\3\7\3\7\3\7") 32 | buf.write("\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\5\7\u00d1") 33 | buf.write("\n\7\3\7\3\7\3\b\3\b\3\b\3\b\3\b\3\b\7\b\u00db\n\b\f\b") 34 | buf.write("\16\b\u00de\13\b\3\b\3\b\3\t\3\t\5\t\u00e4\n\t\3\n\3\n") 35 | buf.write("\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\5\n\u00f1\n\n\3\n") 36 | buf.write("\3\n\3\n\3\n\3\n\3\n\3\n\5\n\u00fa\n\n\3\n\3\n\3\n\3\n") 37 | buf.write("\3\n\5\n\u0101\n\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\5\n") 38 | buf.write("\u010b\n\n\3\n\3\n\6\n\u010f\n\n\r\n\16\n\u0110\5\n\u0113") 39 | buf.write("\n\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\r\3") 40 | buf.write("\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16\3\16") 41 | buf.write("\3\16\3\16\3\17\3\17\3\17\3\17\3\17\3\20\3\20\3\20\3\20") 42 | buf.write("\3\20\3\20\3\20\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\21") 43 | buf.write("\3\21\3\21\3\21\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\22") 44 | buf.write("\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\23\3\23\3\24\3\24") 45 | buf.write("\3\24\3\24\3\24\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26") 46 | buf.write("\3\26\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30") 47 | buf.write("\3\30\3\30\3\30\3\31\3\31\3\31\3\31\3\31\3\32\3\32\3\32") 48 | buf.write("\3\32\3\32\3\32\3\33\3\33\3\33\3\33\3\33\3\33\3\33\3\34") 49 | buf.write("\3\34\3\34\3\34\3\34\3\34\3\34\3\34\3\35\3\35\3\35\3\35") 50 | buf.write("\3\35\3\35\3\36\3\36\7\36\u0193\n\36\f\36\16\36\u0196") 51 | buf.write("\13\36\3\37\3\37\7\37\u019a\n\37\f\37\16\37\u019d\13\37") 52 | buf.write("\3 \3 \3 \5 \u01a2\n \3!\3!\3!\3!\5!\u01a8\n!\3\"\3\"") 53 | buf.write("\3#\3#\3#\3$\3$\3%\3%\3%\3&\3&\3\'\3\'\3(\3(\3)\3)\3*") 54 | buf.write("\3*\3+\3+\3,\3,\3-\3-\3.\3.\3/\3/\3\60\3\60\3\61\3\61") 55 | buf.write("\3\62\3\62\3\63\3\63\3\64\3\64\3\65\3\65\3\65\3\66\3\66") 56 | buf.write("\3\67\6\67\u01d8\n\67\r\67\16\67\u01d9\3\67\3\67\38\3") 57 | buf.write("8\38\38\78\u01e2\n8\f8\168\u01e5\138\38\38\38\38\38\3") 58 | buf.write("9\39\39\39\79\u01f0\n9\f9\169\u01f3\139\39\39\3:\3:\3") 59 | buf.write("\u01e3\2;\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25") 60 | buf.write("\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+") 61 | buf.write("\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E") 62 | buf.write("$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\2i\2k\2") 63 | buf.write("m\65o\66q\67s8\3\2\21\3\2\63;\3\2\62;\4\2))^^\3\2\62\64") 64 | buf.write("\3\2\63\64\3\2\62\63\3\2\62\65\3\2\62\67\5\2C\\aac|\6") 65 | buf.write("\2\62;C\\aac|\7\2//\62;C\\aac|\5\2\62;CHch\6\2--\61;C") 66 | buf.write("\\c|\f\2\13\17\"\"\u0087\u0087\u00a2\u00a2\u1682\u1682") 67 | buf.write("\u2002\u200c\u202a\u202b\u2031\u2031\u2061\u2061\u3002") 68 | buf.write("\u3002\4\2\f\f\17\17\2\u0214\2\3\3\2\2\2\2\5\3\2\2\2\2") 69 | buf.write("\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3") 70 | buf.write("\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2") 71 | buf.write("\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2") 72 | buf.write("\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2") 73 | buf.write("\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63") 74 | buf.write("\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2") 75 | buf.write("\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2") 76 | buf.write("\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3") 77 | buf.write("\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y") 78 | buf.write("\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2") 79 | buf.write("c\3\2\2\2\2e\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2") 80 | buf.write("\2s\3\2\2\2\3u\3\2\2\2\5\u0081\3\2\2\2\7\u008d\3\2\2\2") 81 | buf.write("\t\u009b\3\2\2\2\13\u00a9\3\2\2\2\r\u00b3\3\2\2\2\17\u00d4") 82 | buf.write("\3\2\2\2\21\u00e3\3\2\2\2\23\u00e5\3\2\2\2\25\u0117\3") 83 | buf.write("\2\2\2\27\u011b\3\2\2\2\31\u011e\3\2\2\2\33\u0122\3\2") 84 | buf.write("\2\2\35\u012d\3\2\2\2\37\u0132\3\2\2\2!\u013a\3\2\2\2") 85 | buf.write("#\u0145\3\2\2\2%\u014e\3\2\2\2\'\u0155\3\2\2\2)\u015a") 86 | buf.write("\3\2\2\2+\u015d\3\2\2\2-\u0163\3\2\2\2/\u0168\3\2\2\2") 87 | buf.write("\61\u0170\3\2\2\2\63\u0175\3\2\2\2\65\u017b\3\2\2\2\67") 88 | buf.write("\u0182\3\2\2\29\u018a\3\2\2\2;\u0190\3\2\2\2=\u0197\3") 89 | buf.write("\2\2\2?\u01a1\3\2\2\2A\u01a7\3\2\2\2C\u01a9\3\2\2\2E\u01ab") 90 | buf.write("\3\2\2\2G\u01ae\3\2\2\2I\u01b0\3\2\2\2K\u01b3\3\2\2\2") 91 | buf.write("M\u01b5\3\2\2\2O\u01b7\3\2\2\2Q\u01b9\3\2\2\2S\u01bb\3") 92 | buf.write("\2\2\2U\u01bd\3\2\2\2W\u01bf\3\2\2\2Y\u01c1\3\2\2\2[\u01c3") 93 | buf.write("\3\2\2\2]\u01c5\3\2\2\2_\u01c7\3\2\2\2a\u01c9\3\2\2\2") 94 | buf.write("c\u01cb\3\2\2\2e\u01cd\3\2\2\2g\u01cf\3\2\2\2i\u01d1\3") 95 | buf.write("\2\2\2k\u01d4\3\2\2\2m\u01d7\3\2\2\2o\u01dd\3\2\2\2q\u01eb") 96 | buf.write("\3\2\2\2s\u01f6\3\2\2\2u~\7/\2\2v\177\7\62\2\2w{\t\2\2") 97 | buf.write("\2xz\t\3\2\2yx\3\2\2\2z}\3\2\2\2{y\3\2\2\2{|\3\2\2\2|") 98 | buf.write("\177\3\2\2\2}{\3\2\2\2~v\3\2\2\2~w\3\2\2\2\177\4\3\2\2") 99 | buf.write("\2\u0080\u0082\7-\2\2\u0081\u0080\3\2\2\2\u0081\u0082") 100 | buf.write("\3\2\2\2\u0082\u008b\3\2\2\2\u0083\u008c\7\62\2\2\u0084") 101 | buf.write("\u0088\t\2\2\2\u0085\u0087\t\3\2\2\u0086\u0085\3\2\2\2") 102 | buf.write("\u0087\u008a\3\2\2\2\u0088\u0086\3\2\2\2\u0088\u0089\3") 103 | buf.write("\2\2\2\u0089\u008c\3\2\2\2\u008a\u0088\3\2\2\2\u008b\u0083") 104 | buf.write("\3\2\2\2\u008b\u0084\3\2\2\2\u008c\6\3\2\2\2\u008d\u0091") 105 | buf.write("\7/\2\2\u008e\u0090\t\3\2\2\u008f\u008e\3\2\2\2\u0090") 106 | buf.write("\u0093\3\2\2\2\u0091\u008f\3\2\2\2\u0091\u0092\3\2\2\2") 107 | buf.write("\u0092\u0094\3\2\2\2\u0093\u0091\3\2\2\2\u0094\u0096\7") 108 | buf.write("\60\2\2\u0095\u0097\t\3\2\2\u0096\u0095\3\2\2\2\u0097") 109 | buf.write("\u0098\3\2\2\2\u0098\u0096\3\2\2\2\u0098\u0099\3\2\2\2") 110 | buf.write("\u0099\b\3\2\2\2\u009a\u009c\7-\2\2\u009b\u009a\3\2\2") 111 | buf.write("\2\u009b\u009c\3\2\2\2\u009c\u00a0\3\2\2\2\u009d\u009f") 112 | buf.write("\t\3\2\2\u009e\u009d\3\2\2\2\u009f\u00a2\3\2\2\2\u00a0") 113 | buf.write("\u009e\3\2\2\2\u00a0\u00a1\3\2\2\2\u00a1\u00a3\3\2\2\2") 114 | buf.write("\u00a2\u00a0\3\2\2\2\u00a3\u00a5\7\60\2\2\u00a4\u00a6") 115 | buf.write("\t\3\2\2\u00a5\u00a4\3\2\2\2\u00a6\u00a7\3\2\2\2\u00a7") 116 | buf.write("\u00a5\3\2\2\2\u00a7\u00a8\3\2\2\2\u00a8\n\3\2\2\2\u00a9") 117 | buf.write("\u00aa\7j\2\2\u00aa\u00ae\5K&\2\u00ab\u00ad\5i\65\2\u00ac") 118 | buf.write("\u00ab\3\2\2\2\u00ad\u00b0\3\2\2\2\u00ae\u00ac\3\2\2\2") 119 | buf.write("\u00ae\u00af\3\2\2\2\u00af\u00b1\3\2\2\2\u00b0\u00ae\3") 120 | buf.write("\2\2\2\u00b1\u00b2\5K&\2\u00b2\f\3\2\2\2\u00b3\u00b4\7") 121 | buf.write("d\2\2\u00b4\u00bc\5K&\2\u00b5\u00b6\5k\66\2\u00b6\u00b7") 122 | buf.write("\5k\66\2\u00b7\u00b8\5k\66\2\u00b8\u00b9\5k\66\2\u00b9") 123 | buf.write("\u00bb\3\2\2\2\u00ba\u00b5\3\2\2\2\u00bb\u00be\3\2\2\2") 124 | buf.write("\u00bc\u00ba\3\2\2\2\u00bc\u00bd\3\2\2\2\u00bd\u00d0\3") 125 | buf.write("\2\2\2\u00be\u00bc\3\2\2\2\u00bf\u00c0\5k\66\2\u00c0\u00c1") 126 | buf.write("\5k\66\2\u00c1\u00c2\5k\66\2\u00c2\u00c3\5k\66\2\u00c3") 127 | buf.write("\u00d1\3\2\2\2\u00c4\u00c5\5k\66\2\u00c5\u00c6\5k\66\2") 128 | buf.write("\u00c6\u00c7\5k\66\2\u00c7\u00c8\3\2\2\2\u00c8\u00c9\7") 129 | buf.write("?\2\2\u00c9\u00d1\3\2\2\2\u00ca\u00cb\5k\66\2\u00cb\u00cc") 130 | buf.write("\5k\66\2\u00cc\u00cd\3\2\2\2\u00cd\u00ce\7?\2\2\u00ce") 131 | buf.write("\u00cf\7?\2\2\u00cf\u00d1\3\2\2\2\u00d0\u00bf\3\2\2\2") 132 | buf.write("\u00d0\u00c4\3\2\2\2\u00d0\u00ca\3\2\2\2\u00d1\u00d2\3") 133 | buf.write("\2\2\2\u00d2\u00d3\5K&\2\u00d3\16\3\2\2\2\u00d4\u00dc") 134 | buf.write("\5K&\2\u00d5\u00db\n\4\2\2\u00d6\u00d7\7^\2\2\u00d7\u00db") 135 | buf.write("\7)\2\2\u00d8\u00d9\7^\2\2\u00d9\u00db\7^\2\2\u00da\u00d5") 136 | buf.write("\3\2\2\2\u00da\u00d6\3\2\2\2\u00da\u00d8\3\2\2\2\u00db") 137 | buf.write("\u00de\3\2\2\2\u00dc\u00da\3\2\2\2\u00dc\u00dd\3\2\2\2") 138 | buf.write("\u00dd\u00df\3\2\2\2\u00de\u00dc\3\2\2\2\u00df\u00e0\5") 139 | buf.write("K&\2\u00e0\20\3\2\2\2\u00e1\u00e4\5\61\31\2\u00e2\u00e4") 140 | buf.write("\5\63\32\2\u00e3\u00e1\3\2\2\2\u00e3\u00e2\3\2\2\2\u00e4") 141 | buf.write("\22\3\2\2\2\u00e5\u00e6\7v\2\2\u00e6\u00e7\5K&\2\u00e7") 142 | buf.write("\u00e8\t\3\2\2\u00e8\u00e9\t\3\2\2\u00e9\u00ea\t\3\2\2") 143 | buf.write("\u00ea\u00eb\t\3\2\2\u00eb\u00f0\5]/\2\u00ec\u00ed\7\62") 144 | buf.write("\2\2\u00ed\u00f1\t\2\2\2\u00ee\u00ef\7\63\2\2\u00ef\u00f1") 145 | buf.write("\t\5\2\2\u00f0\u00ec\3\2\2\2\u00f0\u00ee\3\2\2\2\u00f1") 146 | buf.write("\u00f2\3\2\2\2\u00f2\u00f9\5]/\2\u00f3\u00f4\7\62\2\2") 147 | buf.write("\u00f4\u00fa\t\2\2\2\u00f5\u00f6\t\6\2\2\u00f6\u00fa\t") 148 | buf.write("\3\2\2\u00f7\u00f8\7\65\2\2\u00f8\u00fa\t\7\2\2\u00f9") 149 | buf.write("\u00f3\3\2\2\2\u00f9\u00f5\3\2\2\2\u00f9\u00f7\3\2\2\2") 150 | buf.write("\u00fa\u00fb\3\2\2\2\u00fb\u0100\7V\2\2\u00fc\u00fd\t") 151 | buf.write("\7\2\2\u00fd\u0101\t\3\2\2\u00fe\u00ff\7\64\2\2\u00ff") 152 | buf.write("\u0101\t\b\2\2\u0100\u00fc\3\2\2\2\u0100\u00fe\3\2\2\2") 153 | buf.write("\u0101\u0102\3\2\2\2\u0102\u0103\5M\'\2\u0103\u0104\t") 154 | buf.write("\t\2\2\u0104\u0105\t\3\2\2\u0105\u010a\5M\'\2\u0106\u0107") 155 | buf.write("\t\t\2\2\u0107\u010b\t\3\2\2\u0108\u0109\78\2\2\u0109") 156 | buf.write("\u010b\7\62\2\2\u010a\u0106\3\2\2\2\u010a\u0108\3\2\2") 157 | buf.write("\2\u010b\u0112\3\2\2\2\u010c\u010e\5O(\2\u010d\u010f\t") 158 | buf.write("\3\2\2\u010e\u010d\3\2\2\2\u010f\u0110\3\2\2\2\u0110\u010e") 159 | buf.write("\3\2\2\2\u0110\u0111\3\2\2\2\u0111\u0113\3\2\2\2\u0112") 160 | buf.write("\u010c\3\2\2\2\u0112\u0113\3\2\2\2\u0113\u0114\3\2\2\2") 161 | buf.write("\u0114\u0115\7\\\2\2\u0115\u0116\5K&\2\u0116\24\3\2\2") 162 | buf.write("\2\u0117\u0118\7C\2\2\u0118\u0119\7P\2\2\u0119\u011a\7") 163 | buf.write("F\2\2\u011a\26\3\2\2\2\u011b\u011c\7Q\2\2\u011c\u011d") 164 | buf.write("\7T\2\2\u011d\30\3\2\2\2\u011e\u011f\7P\2\2\u011f\u0120") 165 | buf.write("\7Q\2\2\u0120\u0121\7V\2\2\u0121\32\3\2\2\2\u0122\u0123") 166 | buf.write("\7H\2\2\u0123\u0124\7Q\2\2\u0124\u0125\7N\2\2\u0125\u0126") 167 | buf.write("\7N\2\2\u0126\u0127\7Q\2\2\u0127\u0128\7Y\2\2\u0128\u0129") 168 | buf.write("\7G\2\2\u0129\u012a\7F\2\2\u012a\u012b\7D\2\2\u012b\u012c") 169 | buf.write("\7[\2\2\u012c\34\3\2\2\2\u012d\u012e\7N\2\2\u012e\u012f") 170 | buf.write("\7K\2\2\u012f\u0130\7M\2\2\u0130\u0131\7G\2\2\u0131\36") 171 | buf.write("\3\2\2\2\u0132\u0133\7O\2\2\u0133\u0134\7C\2\2\u0134\u0135") 172 | buf.write("\7V\2\2\u0135\u0136\7E\2\2\u0136\u0137\7J\2\2\u0137\u0138") 173 | buf.write("\7G\2\2\u0138\u0139\7U\2\2\u0139 \3\2\2\2\u013a\u013b") 174 | buf.write("\7K\2\2\u013b\u013c\7U\2\2\u013c\u013d\7U\2\2\u013d\u013e") 175 | buf.write("\7W\2\2\u013e\u013f\7R\2\2\u013f\u0140\7G\2\2\u0140\u0141") 176 | buf.write("\7T\2\2\u0141\u0142\7U\2\2\u0142\u0143\7G\2\2\u0143\u0144") 177 | buf.write("\7V\2\2\u0144\"\3\2\2\2\u0145\u0146\7K\2\2\u0146\u0147") 178 | buf.write("\7U\2\2\u0147\u0148\7U\2\2\u0148\u0149\7W\2\2\u0149\u014a") 179 | buf.write("\7D\2\2\u014a\u014b\7U\2\2\u014b\u014c\7G\2\2\u014c\u014d") 180 | buf.write("\7V\2\2\u014d$\3\2\2\2\u014e\u014f\7G\2\2\u014f\u0150") 181 | buf.write("\7Z\2\2\u0150\u0151\7K\2\2\u0151\u0152\7U\2\2\u0152\u0153") 182 | buf.write("\7V\2\2\u0153\u0154\7U\2\2\u0154&\3\2\2\2\u0155\u0156") 183 | buf.write("\7N\2\2\u0156\u0157\7C\2\2\u0157\u0158\7U\2\2\u0158\u0159") 184 | buf.write("\7V\2\2\u0159(\3\2\2\2\u015a\u015b\7K\2\2\u015b\u015c") 185 | buf.write("\7P\2\2\u015c*\3\2\2\2\u015d\u015e\7U\2\2\u015e\u015f") 186 | buf.write("\7V\2\2\u015f\u0160\7C\2\2\u0160\u0161\7T\2\2\u0161\u0162") 187 | buf.write("\7V\2\2\u0162,\3\2\2\2\u0163\u0164\7U\2\2\u0164\u0165") 188 | buf.write("\7V\2\2\u0165\u0166\7Q\2\2\u0166\u0167\7R\2\2\u0167.\3") 189 | buf.write("\2\2\2\u0168\u0169\7U\2\2\u0169\u016a\7G\2\2\u016a\u016b") 190 | buf.write("\7E\2\2\u016b\u016c\7Q\2\2\u016c\u016d\7P\2\2\u016d\u016e") 191 | buf.write("\7F\2\2\u016e\u016f\7U\2\2\u016f\60\3\2\2\2\u0170\u0171") 192 | buf.write("\7v\2\2\u0171\u0172\7t\2\2\u0172\u0173\7w\2\2\u0173\u0174") 193 | buf.write("\7g\2\2\u0174\62\3\2\2\2\u0175\u0176\7h\2\2\u0176\u0177") 194 | buf.write("\7c\2\2\u0177\u0178\7n\2\2\u0178\u0179\7u\2\2\u0179\u017a") 195 | buf.write("\7g\2\2\u017a\64\3\2\2\2\u017b\u017c\7Y\2\2\u017c\u017d") 196 | buf.write("\7K\2\2\u017d\u017e\7V\2\2\u017e\u017f\7J\2\2\u017f\u0180") 197 | buf.write("\7K\2\2\u0180\u0181\7P\2\2\u0181\66\3\2\2\2\u0182\u0183") 198 | buf.write("\7T\2\2\u0183\u0184\7G\2\2\u0184\u0185\7R\2\2\u0185\u0186") 199 | buf.write("\7G\2\2\u0186\u0187\7C\2\2\u0187\u0188\7V\2\2\u0188\u0189") 200 | buf.write("\7U\2\2\u01898\3\2\2\2\u018a\u018b\7V\2\2\u018b\u018c") 201 | buf.write("\7K\2\2\u018c\u018d\7O\2\2\u018d\u018e\7G\2\2\u018e\u018f") 202 | buf.write("\7U\2\2\u018f:\3\2\2\2\u0190\u0194\t\n\2\2\u0191\u0193") 203 | buf.write("\t\13\2\2\u0192\u0191\3\2\2\2\u0193\u0196\3\2\2\2\u0194") 204 | buf.write("\u0192\3\2\2\2\u0194\u0195\3\2\2\2\u0195<\3\2\2\2\u0196") 205 | buf.write("\u0194\3\2\2\2\u0197\u019b\t\n\2\2\u0198\u019a\t\f\2\2") 206 | buf.write("\u0199\u0198\3\2\2\2\u019a\u019d\3\2\2\2\u019b\u0199\3") 207 | buf.write("\2\2\2\u019b\u019c\3\2\2\2\u019c>\3\2\2\2\u019d\u019b") 208 | buf.write("\3\2\2\2\u019e\u01a2\7?\2\2\u019f\u01a0\7?\2\2\u01a0\u01a2") 209 | buf.write("\7?\2\2\u01a1\u019e\3\2\2\2\u01a1\u019f\3\2\2\2\u01a2") 210 | buf.write("@\3\2\2\2\u01a3\u01a4\7#\2\2\u01a4\u01a8\7?\2\2\u01a5") 211 | buf.write("\u01a6\7>\2\2\u01a6\u01a8\7@\2\2\u01a7\u01a3\3\2\2\2\u01a7") 212 | buf.write("\u01a5\3\2\2\2\u01a8B\3\2\2\2\u01a9\u01aa\7>\2\2\u01aa") 213 | buf.write("D\3\2\2\2\u01ab\u01ac\7>\2\2\u01ac\u01ad\7?\2\2\u01ad") 214 | buf.write("F\3\2\2\2\u01ae\u01af\7@\2\2\u01afH\3\2\2\2\u01b0\u01b1") 215 | buf.write("\7@\2\2\u01b1\u01b2\7?\2\2\u01b2J\3\2\2\2\u01b3\u01b4") 216 | buf.write("\7)\2\2\u01b4L\3\2\2\2\u01b5\u01b6\7<\2\2\u01b6N\3\2\2") 217 | buf.write("\2\u01b7\u01b8\7\60\2\2\u01b8P\3\2\2\2\u01b9\u01ba\7.") 218 | buf.write("\2\2\u01baR\3\2\2\2\u01bb\u01bc\7+\2\2\u01bcT\3\2\2\2") 219 | buf.write("\u01bd\u01be\7*\2\2\u01beV\3\2\2\2\u01bf\u01c0\7_\2\2") 220 | buf.write("\u01c0X\3\2\2\2\u01c1\u01c2\7]\2\2\u01c2Z\3\2\2\2\u01c3") 221 | buf.write("\u01c4\7-\2\2\u01c4\\\3\2\2\2\u01c5\u01c6\5_\60\2\u01c6") 222 | buf.write("^\3\2\2\2\u01c7\u01c8\7/\2\2\u01c8`\3\2\2\2\u01c9\u01ca") 223 | buf.write("\7`\2\2\u01cab\3\2\2\2\u01cb\u01cc\7\61\2\2\u01ccd\3\2") 224 | buf.write("\2\2\u01cd\u01ce\7,\2\2\u01cef\3\2\2\2\u01cf\u01d0\t\r") 225 | buf.write("\2\2\u01d0h\3\2\2\2\u01d1\u01d2\5g\64\2\u01d2\u01d3\5") 226 | buf.write("g\64\2\u01d3j\3\2\2\2\u01d4\u01d5\t\16\2\2\u01d5l\3\2") 227 | buf.write("\2\2\u01d6\u01d8\t\17\2\2\u01d7\u01d6\3\2\2\2\u01d8\u01d9") 228 | buf.write("\3\2\2\2\u01d9\u01d7\3\2\2\2\u01d9\u01da\3\2\2\2\u01da") 229 | buf.write("\u01db\3\2\2\2\u01db\u01dc\b\67\2\2\u01dcn\3\2\2\2\u01dd") 230 | buf.write("\u01de\7\61\2\2\u01de\u01df\7,\2\2\u01df\u01e3\3\2\2\2") 231 | buf.write("\u01e0\u01e2\13\2\2\2\u01e1\u01e0\3\2\2\2\u01e2\u01e5") 232 | buf.write("\3\2\2\2\u01e3\u01e4\3\2\2\2\u01e3\u01e1\3\2\2\2\u01e4") 233 | buf.write("\u01e6\3\2\2\2\u01e5\u01e3\3\2\2\2\u01e6\u01e7\7,\2\2") 234 | buf.write("\u01e7\u01e8\7\61\2\2\u01e8\u01e9\3\2\2\2\u01e9\u01ea") 235 | buf.write("\b8\2\2\u01eap\3\2\2\2\u01eb\u01ec\7\61\2\2\u01ec\u01ed") 236 | buf.write("\7\61\2\2\u01ed\u01f1\3\2\2\2\u01ee\u01f0\n\20\2\2\u01ef") 237 | buf.write("\u01ee\3\2\2\2\u01f0\u01f3\3\2\2\2\u01f1\u01ef\3\2\2\2") 238 | buf.write("\u01f1\u01f2\3\2\2\2\u01f2\u01f4\3\2\2\2\u01f3\u01f1\3") 239 | buf.write("\2\2\2\u01f4\u01f5\b9\2\2\u01f5r\3\2\2\2\u01f6\u01f7\13") 240 | buf.write("\2\2\2\u01f7t\3\2\2\2 \2{~\u0081\u0088\u008b\u0091\u0098") 241 | buf.write("\u009b\u00a0\u00a7\u00ae\u00bc\u00d0\u00da\u00dc\u00e3") 242 | buf.write("\u00f0\u00f9\u0100\u010a\u0110\u0112\u0194\u019b\u01a1") 243 | buf.write("\u01a7\u01d9\u01e3\u01f1\3\b\2\2") 244 | return buf.getvalue() 245 | 246 | 247 | class STIXPatternLexer(Lexer): 248 | 249 | atn = ATNDeserializer().deserialize(serializedATN()) 250 | 251 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 252 | 253 | IntNegLiteral = 1 254 | IntPosLiteral = 2 255 | FloatNegLiteral = 3 256 | FloatPosLiteral = 4 257 | HexLiteral = 5 258 | BinaryLiteral = 6 259 | StringLiteral = 7 260 | BoolLiteral = 8 261 | TimestampLiteral = 9 262 | AND = 10 263 | OR = 11 264 | NOT = 12 265 | FOLLOWEDBY = 13 266 | LIKE = 14 267 | MATCHES = 15 268 | ISSUPERSET = 16 269 | ISSUBSET = 17 270 | EXISTS = 18 271 | LAST = 19 272 | IN = 20 273 | START = 21 274 | STOP = 22 275 | SECONDS = 23 276 | TRUE = 24 277 | FALSE = 25 278 | WITHIN = 26 279 | REPEATS = 27 280 | TIMES = 28 281 | IdentifierWithoutHyphen = 29 282 | IdentifierWithHyphen = 30 283 | EQ = 31 284 | NEQ = 32 285 | LT = 33 286 | LE = 34 287 | GT = 35 288 | GE = 36 289 | QUOTE = 37 290 | COLON = 38 291 | DOT = 39 292 | COMMA = 40 293 | RPAREN = 41 294 | LPAREN = 42 295 | RBRACK = 43 296 | LBRACK = 44 297 | PLUS = 45 298 | HYPHEN = 46 299 | MINUS = 47 300 | POWER_OP = 48 301 | DIVIDE = 49 302 | ASTERISK = 50 303 | WS = 51 304 | COMMENT = 52 305 | LINE_COMMENT = 53 306 | InvalidCharacter = 54 307 | 308 | channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] 309 | 310 | modeNames = [ "DEFAULT_MODE" ] 311 | 312 | literalNames = [ "", 313 | "'AND'", "'OR'", "'NOT'", "'FOLLOWEDBY'", "'LIKE'", "'MATCHES'", 314 | "'ISSUPERSET'", "'ISSUBSET'", "'EXISTS'", "'LAST'", "'IN'", 315 | "'START'", "'STOP'", "'SECONDS'", "'true'", "'false'", "'WITHIN'", 316 | "'REPEATS'", "'TIMES'", "'<'", "'<='", "'>'", "'>='", "'''", 317 | "':'", "'.'", "','", "')'", "'('", "']'", "'['", "'+'", "'-'", 318 | "'^'", "'/'", "'*'" ] 319 | 320 | symbolicNames = [ "", 321 | "IntNegLiteral", "IntPosLiteral", "FloatNegLiteral", "FloatPosLiteral", 322 | "HexLiteral", "BinaryLiteral", "StringLiteral", "BoolLiteral", 323 | "TimestampLiteral", "AND", "OR", "NOT", "FOLLOWEDBY", "LIKE", 324 | "MATCHES", "ISSUPERSET", "ISSUBSET", "EXISTS", "LAST", "IN", 325 | "START", "STOP", "SECONDS", "TRUE", "FALSE", "WITHIN", "REPEATS", 326 | "TIMES", "IdentifierWithoutHyphen", "IdentifierWithHyphen", 327 | "EQ", "NEQ", "LT", "LE", "GT", "GE", "QUOTE", "COLON", "DOT", 328 | "COMMA", "RPAREN", "LPAREN", "RBRACK", "LBRACK", "PLUS", "HYPHEN", 329 | "MINUS", "POWER_OP", "DIVIDE", "ASTERISK", "WS", "COMMENT", 330 | "LINE_COMMENT", "InvalidCharacter" ] 331 | 332 | ruleNames = [ "IntNegLiteral", "IntPosLiteral", "FloatNegLiteral", "FloatPosLiteral", 333 | "HexLiteral", "BinaryLiteral", "StringLiteral", "BoolLiteral", 334 | "TimestampLiteral", "AND", "OR", "NOT", "FOLLOWEDBY", 335 | "LIKE", "MATCHES", "ISSUPERSET", "ISSUBSET", "EXISTS", 336 | "LAST", "IN", "START", "STOP", "SECONDS", "TRUE", "FALSE", 337 | "WITHIN", "REPEATS", "TIMES", "IdentifierWithoutHyphen", 338 | "IdentifierWithHyphen", "EQ", "NEQ", "LT", "LE", "GT", 339 | "GE", "QUOTE", "COLON", "DOT", "COMMA", "RPAREN", "LPAREN", 340 | "RBRACK", "LBRACK", "PLUS", "HYPHEN", "MINUS", "POWER_OP", 341 | "DIVIDE", "ASTERISK", "HexDigit", "TwoHexDigits", "Base64Char", 342 | "WS", "COMMENT", "LINE_COMMENT", "InvalidCharacter" ] 343 | 344 | grammarFileName = "STIXPattern.g4" 345 | 346 | def __init__(self, input=None, output:TextIO = sys.stdout): 347 | super().__init__(input, output) 348 | self.checkVersion("4.9.2") 349 | self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) 350 | self._actions = None 351 | self._predicates = None 352 | 353 | 354 | -------------------------------------------------------------------------------- /stix2patterns/v21/grammars/STIXPatternListener.py: -------------------------------------------------------------------------------- 1 | # Generated from STIXPattern.g4 by ANTLR 4.9.2 2 | from antlr4 import * 3 | if __name__ is not None and "." in __name__: 4 | from .STIXPatternParser import STIXPatternParser 5 | else: 6 | from STIXPatternParser import STIXPatternParser 7 | 8 | # This class defines a complete listener for a parse tree produced by STIXPatternParser. 9 | class STIXPatternListener(ParseTreeListener): 10 | 11 | # Enter a parse tree produced by STIXPatternParser#pattern. 12 | def enterPattern(self, ctx:STIXPatternParser.PatternContext): 13 | pass 14 | 15 | # Exit a parse tree produced by STIXPatternParser#pattern. 16 | def exitPattern(self, ctx:STIXPatternParser.PatternContext): 17 | pass 18 | 19 | 20 | # Enter a parse tree produced by STIXPatternParser#observationExpressions. 21 | def enterObservationExpressions(self, ctx:STIXPatternParser.ObservationExpressionsContext): 22 | pass 23 | 24 | # Exit a parse tree produced by STIXPatternParser#observationExpressions. 25 | def exitObservationExpressions(self, ctx:STIXPatternParser.ObservationExpressionsContext): 26 | pass 27 | 28 | 29 | # Enter a parse tree produced by STIXPatternParser#observationExpressionOr. 30 | def enterObservationExpressionOr(self, ctx:STIXPatternParser.ObservationExpressionOrContext): 31 | pass 32 | 33 | # Exit a parse tree produced by STIXPatternParser#observationExpressionOr. 34 | def exitObservationExpressionOr(self, ctx:STIXPatternParser.ObservationExpressionOrContext): 35 | pass 36 | 37 | 38 | # Enter a parse tree produced by STIXPatternParser#observationExpressionAnd. 39 | def enterObservationExpressionAnd(self, ctx:STIXPatternParser.ObservationExpressionAndContext): 40 | pass 41 | 42 | # Exit a parse tree produced by STIXPatternParser#observationExpressionAnd. 43 | def exitObservationExpressionAnd(self, ctx:STIXPatternParser.ObservationExpressionAndContext): 44 | pass 45 | 46 | 47 | # Enter a parse tree produced by STIXPatternParser#observationExpressionRepeated. 48 | def enterObservationExpressionRepeated(self, ctx:STIXPatternParser.ObservationExpressionRepeatedContext): 49 | pass 50 | 51 | # Exit a parse tree produced by STIXPatternParser#observationExpressionRepeated. 52 | def exitObservationExpressionRepeated(self, ctx:STIXPatternParser.ObservationExpressionRepeatedContext): 53 | pass 54 | 55 | 56 | # Enter a parse tree produced by STIXPatternParser#observationExpressionSimple. 57 | def enterObservationExpressionSimple(self, ctx:STIXPatternParser.ObservationExpressionSimpleContext): 58 | pass 59 | 60 | # Exit a parse tree produced by STIXPatternParser#observationExpressionSimple. 61 | def exitObservationExpressionSimple(self, ctx:STIXPatternParser.ObservationExpressionSimpleContext): 62 | pass 63 | 64 | 65 | # Enter a parse tree produced by STIXPatternParser#observationExpressionCompound. 66 | def enterObservationExpressionCompound(self, ctx:STIXPatternParser.ObservationExpressionCompoundContext): 67 | pass 68 | 69 | # Exit a parse tree produced by STIXPatternParser#observationExpressionCompound. 70 | def exitObservationExpressionCompound(self, ctx:STIXPatternParser.ObservationExpressionCompoundContext): 71 | pass 72 | 73 | 74 | # Enter a parse tree produced by STIXPatternParser#observationExpressionWithin. 75 | def enterObservationExpressionWithin(self, ctx:STIXPatternParser.ObservationExpressionWithinContext): 76 | pass 77 | 78 | # Exit a parse tree produced by STIXPatternParser#observationExpressionWithin. 79 | def exitObservationExpressionWithin(self, ctx:STIXPatternParser.ObservationExpressionWithinContext): 80 | pass 81 | 82 | 83 | # Enter a parse tree produced by STIXPatternParser#observationExpressionStartStop. 84 | def enterObservationExpressionStartStop(self, ctx:STIXPatternParser.ObservationExpressionStartStopContext): 85 | pass 86 | 87 | # Exit a parse tree produced by STIXPatternParser#observationExpressionStartStop. 88 | def exitObservationExpressionStartStop(self, ctx:STIXPatternParser.ObservationExpressionStartStopContext): 89 | pass 90 | 91 | 92 | # Enter a parse tree produced by STIXPatternParser#comparisonExpression. 93 | def enterComparisonExpression(self, ctx:STIXPatternParser.ComparisonExpressionContext): 94 | pass 95 | 96 | # Exit a parse tree produced by STIXPatternParser#comparisonExpression. 97 | def exitComparisonExpression(self, ctx:STIXPatternParser.ComparisonExpressionContext): 98 | pass 99 | 100 | 101 | # Enter a parse tree produced by STIXPatternParser#comparisonExpressionAnd. 102 | def enterComparisonExpressionAnd(self, ctx:STIXPatternParser.ComparisonExpressionAndContext): 103 | pass 104 | 105 | # Exit a parse tree produced by STIXPatternParser#comparisonExpressionAnd. 106 | def exitComparisonExpressionAnd(self, ctx:STIXPatternParser.ComparisonExpressionAndContext): 107 | pass 108 | 109 | 110 | # Enter a parse tree produced by STIXPatternParser#propTestEqual. 111 | def enterPropTestEqual(self, ctx:STIXPatternParser.PropTestEqualContext): 112 | pass 113 | 114 | # Exit a parse tree produced by STIXPatternParser#propTestEqual. 115 | def exitPropTestEqual(self, ctx:STIXPatternParser.PropTestEqualContext): 116 | pass 117 | 118 | 119 | # Enter a parse tree produced by STIXPatternParser#propTestOrder. 120 | def enterPropTestOrder(self, ctx:STIXPatternParser.PropTestOrderContext): 121 | pass 122 | 123 | # Exit a parse tree produced by STIXPatternParser#propTestOrder. 124 | def exitPropTestOrder(self, ctx:STIXPatternParser.PropTestOrderContext): 125 | pass 126 | 127 | 128 | # Enter a parse tree produced by STIXPatternParser#propTestSet. 129 | def enterPropTestSet(self, ctx:STIXPatternParser.PropTestSetContext): 130 | pass 131 | 132 | # Exit a parse tree produced by STIXPatternParser#propTestSet. 133 | def exitPropTestSet(self, ctx:STIXPatternParser.PropTestSetContext): 134 | pass 135 | 136 | 137 | # Enter a parse tree produced by STIXPatternParser#propTestLike. 138 | def enterPropTestLike(self, ctx:STIXPatternParser.PropTestLikeContext): 139 | pass 140 | 141 | # Exit a parse tree produced by STIXPatternParser#propTestLike. 142 | def exitPropTestLike(self, ctx:STIXPatternParser.PropTestLikeContext): 143 | pass 144 | 145 | 146 | # Enter a parse tree produced by STIXPatternParser#propTestRegex. 147 | def enterPropTestRegex(self, ctx:STIXPatternParser.PropTestRegexContext): 148 | pass 149 | 150 | # Exit a parse tree produced by STIXPatternParser#propTestRegex. 151 | def exitPropTestRegex(self, ctx:STIXPatternParser.PropTestRegexContext): 152 | pass 153 | 154 | 155 | # Enter a parse tree produced by STIXPatternParser#propTestIsSubset. 156 | def enterPropTestIsSubset(self, ctx:STIXPatternParser.PropTestIsSubsetContext): 157 | pass 158 | 159 | # Exit a parse tree produced by STIXPatternParser#propTestIsSubset. 160 | def exitPropTestIsSubset(self, ctx:STIXPatternParser.PropTestIsSubsetContext): 161 | pass 162 | 163 | 164 | # Enter a parse tree produced by STIXPatternParser#propTestIsSuperset. 165 | def enterPropTestIsSuperset(self, ctx:STIXPatternParser.PropTestIsSupersetContext): 166 | pass 167 | 168 | # Exit a parse tree produced by STIXPatternParser#propTestIsSuperset. 169 | def exitPropTestIsSuperset(self, ctx:STIXPatternParser.PropTestIsSupersetContext): 170 | pass 171 | 172 | 173 | # Enter a parse tree produced by STIXPatternParser#propTestParen. 174 | def enterPropTestParen(self, ctx:STIXPatternParser.PropTestParenContext): 175 | pass 176 | 177 | # Exit a parse tree produced by STIXPatternParser#propTestParen. 178 | def exitPropTestParen(self, ctx:STIXPatternParser.PropTestParenContext): 179 | pass 180 | 181 | 182 | # Enter a parse tree produced by STIXPatternParser#propTestExists. 183 | def enterPropTestExists(self, ctx:STIXPatternParser.PropTestExistsContext): 184 | pass 185 | 186 | # Exit a parse tree produced by STIXPatternParser#propTestExists. 187 | def exitPropTestExists(self, ctx:STIXPatternParser.PropTestExistsContext): 188 | pass 189 | 190 | 191 | # Enter a parse tree produced by STIXPatternParser#startStopQualifier. 192 | def enterStartStopQualifier(self, ctx:STIXPatternParser.StartStopQualifierContext): 193 | pass 194 | 195 | # Exit a parse tree produced by STIXPatternParser#startStopQualifier. 196 | def exitStartStopQualifier(self, ctx:STIXPatternParser.StartStopQualifierContext): 197 | pass 198 | 199 | 200 | # Enter a parse tree produced by STIXPatternParser#withinQualifier. 201 | def enterWithinQualifier(self, ctx:STIXPatternParser.WithinQualifierContext): 202 | pass 203 | 204 | # Exit a parse tree produced by STIXPatternParser#withinQualifier. 205 | def exitWithinQualifier(self, ctx:STIXPatternParser.WithinQualifierContext): 206 | pass 207 | 208 | 209 | # Enter a parse tree produced by STIXPatternParser#repeatedQualifier. 210 | def enterRepeatedQualifier(self, ctx:STIXPatternParser.RepeatedQualifierContext): 211 | pass 212 | 213 | # Exit a parse tree produced by STIXPatternParser#repeatedQualifier. 214 | def exitRepeatedQualifier(self, ctx:STIXPatternParser.RepeatedQualifierContext): 215 | pass 216 | 217 | 218 | # Enter a parse tree produced by STIXPatternParser#objectPath. 219 | def enterObjectPath(self, ctx:STIXPatternParser.ObjectPathContext): 220 | pass 221 | 222 | # Exit a parse tree produced by STIXPatternParser#objectPath. 223 | def exitObjectPath(self, ctx:STIXPatternParser.ObjectPathContext): 224 | pass 225 | 226 | 227 | # Enter a parse tree produced by STIXPatternParser#objectType. 228 | def enterObjectType(self, ctx:STIXPatternParser.ObjectTypeContext): 229 | pass 230 | 231 | # Exit a parse tree produced by STIXPatternParser#objectType. 232 | def exitObjectType(self, ctx:STIXPatternParser.ObjectTypeContext): 233 | pass 234 | 235 | 236 | # Enter a parse tree produced by STIXPatternParser#firstPathComponent. 237 | def enterFirstPathComponent(self, ctx:STIXPatternParser.FirstPathComponentContext): 238 | pass 239 | 240 | # Exit a parse tree produced by STIXPatternParser#firstPathComponent. 241 | def exitFirstPathComponent(self, ctx:STIXPatternParser.FirstPathComponentContext): 242 | pass 243 | 244 | 245 | # Enter a parse tree produced by STIXPatternParser#indexPathStep. 246 | def enterIndexPathStep(self, ctx:STIXPatternParser.IndexPathStepContext): 247 | pass 248 | 249 | # Exit a parse tree produced by STIXPatternParser#indexPathStep. 250 | def exitIndexPathStep(self, ctx:STIXPatternParser.IndexPathStepContext): 251 | pass 252 | 253 | 254 | # Enter a parse tree produced by STIXPatternParser#pathStep. 255 | def enterPathStep(self, ctx:STIXPatternParser.PathStepContext): 256 | pass 257 | 258 | # Exit a parse tree produced by STIXPatternParser#pathStep. 259 | def exitPathStep(self, ctx:STIXPatternParser.PathStepContext): 260 | pass 261 | 262 | 263 | # Enter a parse tree produced by STIXPatternParser#keyPathStep. 264 | def enterKeyPathStep(self, ctx:STIXPatternParser.KeyPathStepContext): 265 | pass 266 | 267 | # Exit a parse tree produced by STIXPatternParser#keyPathStep. 268 | def exitKeyPathStep(self, ctx:STIXPatternParser.KeyPathStepContext): 269 | pass 270 | 271 | 272 | # Enter a parse tree produced by STIXPatternParser#setLiteral. 273 | def enterSetLiteral(self, ctx:STIXPatternParser.SetLiteralContext): 274 | pass 275 | 276 | # Exit a parse tree produced by STIXPatternParser#setLiteral. 277 | def exitSetLiteral(self, ctx:STIXPatternParser.SetLiteralContext): 278 | pass 279 | 280 | 281 | # Enter a parse tree produced by STIXPatternParser#primitiveLiteral. 282 | def enterPrimitiveLiteral(self, ctx:STIXPatternParser.PrimitiveLiteralContext): 283 | pass 284 | 285 | # Exit a parse tree produced by STIXPatternParser#primitiveLiteral. 286 | def exitPrimitiveLiteral(self, ctx:STIXPatternParser.PrimitiveLiteralContext): 287 | pass 288 | 289 | 290 | # Enter a parse tree produced by STIXPatternParser#orderableLiteral. 291 | def enterOrderableLiteral(self, ctx:STIXPatternParser.OrderableLiteralContext): 292 | pass 293 | 294 | # Exit a parse tree produced by STIXPatternParser#orderableLiteral. 295 | def exitOrderableLiteral(self, ctx:STIXPatternParser.OrderableLiteralContext): 296 | pass 297 | 298 | 299 | 300 | del STIXPatternParser -------------------------------------------------------------------------------- /stix2patterns/v21/grammars/STIXPatternVisitor.py: -------------------------------------------------------------------------------- 1 | # Generated from STIXPattern.g4 by ANTLR 4.9.2 2 | from antlr4 import * 3 | if __name__ is not None and "." in __name__: 4 | from .STIXPatternParser import STIXPatternParser 5 | else: 6 | from STIXPatternParser import STIXPatternParser 7 | 8 | # This class defines a complete generic visitor for a parse tree produced by STIXPatternParser. 9 | 10 | class STIXPatternVisitor(ParseTreeVisitor): 11 | 12 | # Visit a parse tree produced by STIXPatternParser#pattern. 13 | def visitPattern(self, ctx:STIXPatternParser.PatternContext): 14 | return self.visitChildren(ctx) 15 | 16 | 17 | # Visit a parse tree produced by STIXPatternParser#observationExpressions. 18 | def visitObservationExpressions(self, ctx:STIXPatternParser.ObservationExpressionsContext): 19 | return self.visitChildren(ctx) 20 | 21 | 22 | # Visit a parse tree produced by STIXPatternParser#observationExpressionOr. 23 | def visitObservationExpressionOr(self, ctx:STIXPatternParser.ObservationExpressionOrContext): 24 | return self.visitChildren(ctx) 25 | 26 | 27 | # Visit a parse tree produced by STIXPatternParser#observationExpressionAnd. 28 | def visitObservationExpressionAnd(self, ctx:STIXPatternParser.ObservationExpressionAndContext): 29 | return self.visitChildren(ctx) 30 | 31 | 32 | # Visit a parse tree produced by STIXPatternParser#observationExpressionRepeated. 33 | def visitObservationExpressionRepeated(self, ctx:STIXPatternParser.ObservationExpressionRepeatedContext): 34 | return self.visitChildren(ctx) 35 | 36 | 37 | # Visit a parse tree produced by STIXPatternParser#observationExpressionSimple. 38 | def visitObservationExpressionSimple(self, ctx:STIXPatternParser.ObservationExpressionSimpleContext): 39 | return self.visitChildren(ctx) 40 | 41 | 42 | # Visit a parse tree produced by STIXPatternParser#observationExpressionCompound. 43 | def visitObservationExpressionCompound(self, ctx:STIXPatternParser.ObservationExpressionCompoundContext): 44 | return self.visitChildren(ctx) 45 | 46 | 47 | # Visit a parse tree produced by STIXPatternParser#observationExpressionWithin. 48 | def visitObservationExpressionWithin(self, ctx:STIXPatternParser.ObservationExpressionWithinContext): 49 | return self.visitChildren(ctx) 50 | 51 | 52 | # Visit a parse tree produced by STIXPatternParser#observationExpressionStartStop. 53 | def visitObservationExpressionStartStop(self, ctx:STIXPatternParser.ObservationExpressionStartStopContext): 54 | return self.visitChildren(ctx) 55 | 56 | 57 | # Visit a parse tree produced by STIXPatternParser#comparisonExpression. 58 | def visitComparisonExpression(self, ctx:STIXPatternParser.ComparisonExpressionContext): 59 | return self.visitChildren(ctx) 60 | 61 | 62 | # Visit a parse tree produced by STIXPatternParser#comparisonExpressionAnd. 63 | def visitComparisonExpressionAnd(self, ctx:STIXPatternParser.ComparisonExpressionAndContext): 64 | return self.visitChildren(ctx) 65 | 66 | 67 | # Visit a parse tree produced by STIXPatternParser#propTestEqual. 68 | def visitPropTestEqual(self, ctx:STIXPatternParser.PropTestEqualContext): 69 | return self.visitChildren(ctx) 70 | 71 | 72 | # Visit a parse tree produced by STIXPatternParser#propTestOrder. 73 | def visitPropTestOrder(self, ctx:STIXPatternParser.PropTestOrderContext): 74 | return self.visitChildren(ctx) 75 | 76 | 77 | # Visit a parse tree produced by STIXPatternParser#propTestSet. 78 | def visitPropTestSet(self, ctx:STIXPatternParser.PropTestSetContext): 79 | return self.visitChildren(ctx) 80 | 81 | 82 | # Visit a parse tree produced by STIXPatternParser#propTestLike. 83 | def visitPropTestLike(self, ctx:STIXPatternParser.PropTestLikeContext): 84 | return self.visitChildren(ctx) 85 | 86 | 87 | # Visit a parse tree produced by STIXPatternParser#propTestRegex. 88 | def visitPropTestRegex(self, ctx:STIXPatternParser.PropTestRegexContext): 89 | return self.visitChildren(ctx) 90 | 91 | 92 | # Visit a parse tree produced by STIXPatternParser#propTestIsSubset. 93 | def visitPropTestIsSubset(self, ctx:STIXPatternParser.PropTestIsSubsetContext): 94 | return self.visitChildren(ctx) 95 | 96 | 97 | # Visit a parse tree produced by STIXPatternParser#propTestIsSuperset. 98 | def visitPropTestIsSuperset(self, ctx:STIXPatternParser.PropTestIsSupersetContext): 99 | return self.visitChildren(ctx) 100 | 101 | 102 | # Visit a parse tree produced by STIXPatternParser#propTestParen. 103 | def visitPropTestParen(self, ctx:STIXPatternParser.PropTestParenContext): 104 | return self.visitChildren(ctx) 105 | 106 | 107 | # Visit a parse tree produced by STIXPatternParser#propTestExists. 108 | def visitPropTestExists(self, ctx:STIXPatternParser.PropTestExistsContext): 109 | return self.visitChildren(ctx) 110 | 111 | 112 | # Visit a parse tree produced by STIXPatternParser#startStopQualifier. 113 | def visitStartStopQualifier(self, ctx:STIXPatternParser.StartStopQualifierContext): 114 | return self.visitChildren(ctx) 115 | 116 | 117 | # Visit a parse tree produced by STIXPatternParser#withinQualifier. 118 | def visitWithinQualifier(self, ctx:STIXPatternParser.WithinQualifierContext): 119 | return self.visitChildren(ctx) 120 | 121 | 122 | # Visit a parse tree produced by STIXPatternParser#repeatedQualifier. 123 | def visitRepeatedQualifier(self, ctx:STIXPatternParser.RepeatedQualifierContext): 124 | return self.visitChildren(ctx) 125 | 126 | 127 | # Visit a parse tree produced by STIXPatternParser#objectPath. 128 | def visitObjectPath(self, ctx:STIXPatternParser.ObjectPathContext): 129 | return self.visitChildren(ctx) 130 | 131 | 132 | # Visit a parse tree produced by STIXPatternParser#objectType. 133 | def visitObjectType(self, ctx:STIXPatternParser.ObjectTypeContext): 134 | return self.visitChildren(ctx) 135 | 136 | 137 | # Visit a parse tree produced by STIXPatternParser#firstPathComponent. 138 | def visitFirstPathComponent(self, ctx:STIXPatternParser.FirstPathComponentContext): 139 | return self.visitChildren(ctx) 140 | 141 | 142 | # Visit a parse tree produced by STIXPatternParser#indexPathStep. 143 | def visitIndexPathStep(self, ctx:STIXPatternParser.IndexPathStepContext): 144 | return self.visitChildren(ctx) 145 | 146 | 147 | # Visit a parse tree produced by STIXPatternParser#pathStep. 148 | def visitPathStep(self, ctx:STIXPatternParser.PathStepContext): 149 | return self.visitChildren(ctx) 150 | 151 | 152 | # Visit a parse tree produced by STIXPatternParser#keyPathStep. 153 | def visitKeyPathStep(self, ctx:STIXPatternParser.KeyPathStepContext): 154 | return self.visitChildren(ctx) 155 | 156 | 157 | # Visit a parse tree produced by STIXPatternParser#setLiteral. 158 | def visitSetLiteral(self, ctx:STIXPatternParser.SetLiteralContext): 159 | return self.visitChildren(ctx) 160 | 161 | 162 | # Visit a parse tree produced by STIXPatternParser#primitiveLiteral. 163 | def visitPrimitiveLiteral(self, ctx:STIXPatternParser.PrimitiveLiteralContext): 164 | return self.visitChildren(ctx) 165 | 166 | 167 | # Visit a parse tree produced by STIXPatternParser#orderableLiteral. 168 | def visitOrderableLiteral(self, ctx:STIXPatternParser.OrderableLiteralContext): 169 | return self.visitChildren(ctx) 170 | 171 | 172 | 173 | del STIXPatternParser -------------------------------------------------------------------------------- /stix2patterns/v21/grammars/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oasis-open/cti-pattern-validator/86c7dd7c7aa8fc4562f00e649f0ae8c11c673e64/stix2patterns/v21/grammars/__init__.py -------------------------------------------------------------------------------- /stix2patterns/v21/inspector.py: -------------------------------------------------------------------------------- 1 | from ..inspector import INDEX_STAR, _PatternData, _string_literal_to_string 2 | from .grammars.STIXPatternListener import STIXPatternListener 3 | 4 | 5 | class InspectionListener(STIXPatternListener): 6 | """This listener collects info about a pattern and puts it 7 | in a python structure. It is intended to assist apps which wish to 8 | look "inside" a pattern and know what's in there. 9 | """ 10 | 11 | def __init__(self): 12 | self.__comparison_data = {} 13 | self.__qualifiers = set() 14 | self.__observation_ops = set() 15 | self.__obj_type = None 16 | self.__obj_path = None 17 | 18 | def pattern_data(self): 19 | return _PatternData(self.__comparison_data, self.__observation_ops, 20 | self.__qualifiers) 21 | 22 | def __add_prop_tuple(self, obj_type, obj_path, op, value): 23 | if obj_type not in self.__comparison_data: 24 | self.__comparison_data[obj_type] = [] 25 | 26 | self.__comparison_data[obj_type].append((obj_path, op, value)) 27 | 28 | def exitObservationExpressions(self, ctx): 29 | if ctx.FOLLOWEDBY(): 30 | self.__observation_ops.add(u"FOLLOWEDBY") 31 | 32 | def exitObservationExpressionOr(self, ctx): 33 | if ctx.OR(): 34 | self.__observation_ops.add(u"OR") 35 | 36 | def exitObservationExpressionAnd(self, ctx): 37 | if ctx.AND(): 38 | self.__observation_ops.add(u"AND") 39 | 40 | def exitStartStopQualifier(self, ctx): 41 | self.__qualifiers.add( 42 | u"START {0} STOP {1}".format( 43 | ctx.TimestampLiteral(0), ctx.TimestampLiteral(1) 44 | ) 45 | ) 46 | 47 | def exitWithinQualifier(self, ctx): 48 | self.__qualifiers.add( 49 | u"WITHIN {0} SECONDS".format( 50 | ctx.IntPosLiteral() or ctx.FloatPosLiteral() 51 | ) 52 | ) 53 | 54 | def exitRepeatedQualifier(self, ctx): 55 | self.__qualifiers.add( 56 | u"REPEATS {0} TIMES".format( 57 | ctx.IntPosLiteral() 58 | ) 59 | ) 60 | 61 | def exitPropTestEqual(self, ctx): 62 | op_tok = ctx.EQ() or ctx.NEQ() 63 | op_str = u"NOT " if ctx.NOT() else u"" 64 | op_str += op_tok.getText() 65 | 66 | value = ctx.primitiveLiteral().getText() 67 | 68 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 69 | value) 70 | 71 | def exitPropTestOrder(self, ctx): 72 | op_tok = ctx.GT() or ctx.LT() or ctx.GE() or ctx.LE() 73 | op_str = u"NOT " if ctx.NOT() else u"" 74 | op_str += op_tok.getText() 75 | 76 | value = ctx.orderableLiteral().getText() 77 | 78 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 79 | value) 80 | 81 | def exitPropTestSet(self, ctx): 82 | op_str = u"NOT " if ctx.NOT() else u"" 83 | op_str += u"IN" 84 | 85 | value = ctx.setLiteral().getText() 86 | 87 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 88 | value) 89 | 90 | def exitPropTestLike(self, ctx): 91 | op_str = u"NOT " if ctx.NOT() else u"" 92 | op_str += u"LIKE" 93 | 94 | value = ctx.StringLiteral().getText() 95 | 96 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 97 | value) 98 | 99 | def exitPropTestRegex(self, ctx): 100 | op_str = u"NOT " if ctx.NOT() else u"" 101 | op_str += u"MATCHES" 102 | 103 | value = ctx.StringLiteral().getText() 104 | 105 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 106 | value) 107 | 108 | def exitPropTestIsSubset(self, ctx): 109 | op_str = u"NOT " if ctx.NOT() else u"" 110 | op_str += u"ISSUBSET" 111 | 112 | value = ctx.StringLiteral().getText() 113 | 114 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 115 | value) 116 | 117 | def exitPropTestIsSuperset(self, ctx): 118 | op_str = u"NOT " if ctx.NOT() else u"" 119 | op_str += u"ISSUPERSET" 120 | 121 | value = ctx.StringLiteral().getText() 122 | 123 | self.__add_prop_tuple(self.__obj_type, self.__obj_path, op_str, 124 | value) 125 | 126 | def exitObjectType(self, ctx): 127 | self.__obj_type = ctx.getText() 128 | 129 | def exitFirstPathComponent(self, ctx): 130 | if ctx.StringLiteral(): 131 | path_component = _string_literal_to_string(ctx.StringLiteral()) 132 | else: 133 | path_component = ctx.getText() 134 | 135 | self.__obj_path = [path_component] 136 | 137 | def exitKeyPathStep(self, ctx): 138 | if ctx.IdentifierWithoutHyphen(): 139 | path_component = ctx.IdentifierWithoutHyphen().getText() 140 | else: # A StringLiteral 141 | path_component = _string_literal_to_string(ctx.StringLiteral()) 142 | 143 | self.__obj_path.append(path_component) 144 | 145 | def exitIndexPathStep(self, ctx): 146 | if ctx.ASTERISK(): 147 | self.__obj_path.append(INDEX_STAR) 148 | else: 149 | self.__obj_path.append(int(ctx.IntPosLiteral().getText())) 150 | -------------------------------------------------------------------------------- /stix2patterns/v21/object_validator.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import stix2patterns.inspector 4 | 5 | HASHES_REGEX = { 6 | "MD5": (r"^[a-fA-F0-9]{32}$", "MD5"), 7 | "MD6": (r"^[a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{56}|\ 8 | [a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128}$", "MD6"), 9 | "RIPEMD160": (r"^[a-fA-F0-9]{40}$", "RIPEMD-160"), 10 | "SHA1": (r"^[a-fA-F0-9]{40}$", "SHA-1"), 11 | "SHA224": (r"^[a-fA-F0-9]{56}$", "SHA-224"), 12 | "SHA256": (r"^[a-fA-F0-9]{64}$", "SHA-256"), 13 | "SHA384": (r"^[a-fA-F0-9]{96}$", "SHA-384"), 14 | "SHA512": (r"^[a-fA-F0-9]{128}$", "SHA-512"), 15 | "SHA3224": (r"^[a-fA-F0-9]{56}$", "SHA3-224"), 16 | "SHA3256": (r"^[a-fA-F0-9]{64}$", "SHA3-256"), 17 | "SHA3384": (r"^[a-fA-F0-9]{96}$", "SHA3-384"), 18 | "SHA3512": (r"^[a-fA-F0-9]{128}$", "SHA3-512"), 19 | "SSDEEP": (r"^[a-zA-Z0-9/+:.]{1,128}$", "SSDEEP"), 20 | "WHIRLPOOL": (r"^[a-fA-F0-9]{128}$", "WHIRLPOOL"), 21 | } 22 | 23 | 24 | def verify_object(patt_data): 25 | error_list = [] 26 | msg = "FAIL: '{}' is not a valid {} hash" 27 | 28 | # iterate over observed objects 29 | for type_name, comp in patt_data.comparisons.items(): 30 | for obj_path, op, value in comp: 31 | if 'hashes' in obj_path: 32 | hash_selector = obj_path[-1] 33 | if hash_selector is not stix2patterns.inspector.INDEX_STAR: 34 | hash_type = \ 35 | hash_selector.upper().replace('-', '').replace("'", "") 36 | hash_string = value.replace("'", "") 37 | if hash_type in HASHES_REGEX: 38 | if not re.match(HASHES_REGEX[hash_type][0], hash_string): 39 | error_list.append( 40 | msg.format(hash_string, hash_selector) 41 | ) 42 | return error_list 43 | -------------------------------------------------------------------------------- /stix2patterns/v21/pattern.py: -------------------------------------------------------------------------------- 1 | import antlr4 2 | import six 3 | 4 | from ..exceptions import ParseException, ParserErrorListener 5 | from .grammars.STIXPatternLexer import STIXPatternLexer 6 | from .grammars.STIXPatternParser import STIXPatternParser 7 | from .inspector import InspectionListener 8 | 9 | 10 | class Pattern(object): 11 | """ 12 | Represents a pattern in a "compiled" form, for more efficient reuse. 13 | """ 14 | def __init__(self, pattern_str): 15 | """ 16 | Compile a pattern. 17 | 18 | :param pattern_str: The pattern to compile 19 | :raises ParseException: If there is a parse error 20 | """ 21 | self.__parse_tree = self.__do_parse(pattern_str) 22 | 23 | def inspect(self): 24 | """ 25 | Inspect a pattern. This gives information regarding the sorts of 26 | operations, content, etc in use in the pattern. 27 | 28 | :return: Pattern information 29 | """ 30 | 31 | inspector = InspectionListener() 32 | self.walk(inspector) 33 | 34 | return inspector.pattern_data() 35 | 36 | def walk(self, listener): 37 | """Walk the parse tree, using the given listener. The listener 38 | should be a 39 | stix2patterns.grammars.STIXPatternListener.STIXPatternListener (or 40 | subclass) instance.""" 41 | antlr4.ParseTreeWalker.DEFAULT.walk(listener, self.__parse_tree) 42 | 43 | def visit(self, visitor): 44 | """ 45 | Walk the parse tree using the given visitor. 46 | 47 | :param visitor: A visitor object (STIXPatternVisitor instance) 48 | :return: The visitor's return value 49 | """ 50 | return visitor.visit(self.__parse_tree) 51 | 52 | def __do_parse(self, pattern_str): 53 | """ 54 | Parses the given pattern and returns the antlr parse tree. 55 | 56 | :param pattern_str: The STIX pattern 57 | :return: The parse tree 58 | :raises ParseException: If there is a parse error 59 | """ 60 | in_ = antlr4.InputStream(pattern_str) 61 | lexer = STIXPatternLexer(in_) 62 | lexer.removeErrorListeners() # remove the default "console" listener 63 | token_stream = antlr4.CommonTokenStream(lexer) 64 | 65 | parser = STIXPatternParser(token_stream) 66 | parser.removeErrorListeners() # remove the default "console" listener 67 | error_listener = ParserErrorListener() 68 | parser.addErrorListener(error_listener) 69 | 70 | # I found no public API for this... 71 | # The default error handler tries to keep parsing, and I don't 72 | # think that's appropriate here. (These error handlers are only for 73 | # handling the built-in RecognitionException errors.) 74 | parser._errHandler = antlr4.BailErrorStrategy() 75 | 76 | # To improve error messages, replace "" in the literal 77 | # names with symbolic names. This is a hack, but seemed like 78 | # the simplest workaround. 79 | for i, lit_name in enumerate(parser.literalNames): 80 | if lit_name == u"": 81 | parser.literalNames[i] = parser.symbolicNames[i] 82 | 83 | # parser.setTrace(True) 84 | 85 | try: 86 | tree = parser.pattern() 87 | # print(tree.toStringTree(recog=parser)) 88 | 89 | return tree 90 | except antlr4.error.Errors.ParseCancellationException as e: 91 | # The cancellation exception wraps the real RecognitionException 92 | # which caused the parser to bail. 93 | real_exc = e.args[0] 94 | 95 | # I want to bail when the first error is hit. But I also want 96 | # a decent error message. When an error is encountered in 97 | # Parser.match(), the BailErrorStrategy produces the 98 | # ParseCancellationException. It is not a subclass of 99 | # RecognitionException, so none of the 'except' clauses which would 100 | # normally report an error are invoked. 101 | # 102 | # Error message creation is buried in the ErrorStrategy, and I can 103 | # (ab)use the API to get a message: register an error listener with 104 | # the parser, force an error report, then get the message out of the 105 | # listener. Error listener registration is above; now we force its 106 | # invocation. Wish this could be cleaner... 107 | parser._errHandler.reportError(parser, real_exc) 108 | 109 | # should probably chain exceptions if we can... 110 | # Should I report the cancellation or recognition exception as the 111 | # cause...? 112 | six.raise_from(ParseException(error_listener.error_message), 113 | real_exc) 114 | -------------------------------------------------------------------------------- /stix2patterns/v21/validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Validates a user entered pattern against STIXPattern grammar. 3 | """ 4 | 5 | import enum 6 | 7 | from antlr4 import CommonTokenStream, ParseTreeWalker 8 | 9 | from . import object_validator 10 | from ..exceptions import STIXPatternErrorListener 11 | from .grammars.STIXPatternLexer import STIXPatternLexer 12 | from .grammars.STIXPatternListener import STIXPatternListener 13 | from .grammars.STIXPatternParser import STIXPatternParser 14 | from .inspector import InspectionListener 15 | 16 | QualType = enum.Enum("QualType", "WITHIN REPEATS STARTSTOP") 17 | 18 | 19 | class DuplicateQualifierTypeError(Exception): 20 | """ 21 | Instances represent finding multiple qualifiers of the same type directly 22 | applied to an observation expression (i.e. not on some parenthesized group 23 | of which the observation expression is a member). 24 | """ 25 | def __init__(self, qual_type): 26 | """ 27 | Initialize this exception instance. 28 | 29 | :param qual_type: The qualifier type which was found to be duplicated. 30 | Must be a member of the QualType enum. 31 | """ 32 | message = "Duplicate qualifier type encountered: " + qual_type.name 33 | 34 | super(DuplicateQualifierTypeError, self).__init__(message) 35 | 36 | self.qual_type = qual_type 37 | 38 | 39 | class ValidationListener(STIXPatternListener): 40 | """ 41 | Does some pattern validation via a parse tree traversal. 42 | """ 43 | def __init__(self): 44 | self.__qual_types = None 45 | 46 | def __check_qualifier_type(self, qual_type): 47 | if self.__qual_types is not None: 48 | if qual_type in self.__qual_types: 49 | raise DuplicateQualifierTypeError(qual_type) 50 | else: 51 | self.__qual_types.add(qual_type) 52 | 53 | def exitObservationExpressionSimple(self, ctx): 54 | self.__qual_types = set() 55 | 56 | def exitObservationExpressionCompound(self, ctx): 57 | self.__qual_types = None 58 | 59 | def exitObservationExpressionWithin(self, ctx): 60 | self.__check_qualifier_type(QualType.WITHIN) 61 | 62 | def exitObservationExpressionRepeated(self, ctx): 63 | self.__check_qualifier_type(QualType.REPEATS) 64 | 65 | def exitObservationExpressionStartStop(self, ctx): 66 | self.__check_qualifier_type(QualType.STARTSTOP) 67 | 68 | 69 | def run_validator(pattern): 70 | """ 71 | Validates a pattern against the STIX Pattern grammar. Error messages are 72 | returned in a list. The test passed if the returned list is empty. 73 | """ 74 | parseErrListener = STIXPatternErrorListener() 75 | 76 | lexer = STIXPatternLexer(pattern) 77 | # it always adds a console listener by default... remove it. 78 | lexer.removeErrorListeners() 79 | 80 | stream = CommonTokenStream(lexer) 81 | 82 | parser = STIXPatternParser(stream) 83 | 84 | # it always adds a console listener by default... remove it. 85 | parser.removeErrorListeners() 86 | parser.addErrorListener(parseErrListener) 87 | 88 | # To improve error messages, replace "" in the literal 89 | # names with symbolic names. This is a hack, but seemed like 90 | # the simplest workaround. 91 | for i, lit_name in enumerate(parser.literalNames): 92 | if lit_name == u"": 93 | parser.literalNames[i] = parser.symbolicNames[i] 94 | 95 | tree = parser.pattern() 96 | 97 | # validate observed objects 98 | if len(parseErrListener.err_strings) == 0: 99 | inspection_listener = InspectionListener() 100 | ParseTreeWalker.DEFAULT.walk(inspection_listener, tree) 101 | patt_data = inspection_listener.pattern_data() 102 | 103 | # check objects 104 | obj_validator_results = object_validator.verify_object(patt_data) 105 | if obj_validator_results: 106 | parseErrListener.err_strings.extend(obj_validator_results) 107 | 108 | # check qualifiers 109 | try: 110 | ParseTreeWalker.DEFAULT.walk(ValidationListener(), tree) 111 | except DuplicateQualifierTypeError as e: 112 | parseErrListener.err_strings.insert(0, "FAIL: " + e.args[0]) 113 | 114 | return parseErrListener.err_strings 115 | -------------------------------------------------------------------------------- /stix2patterns/validator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Validates a user entered pattern against STIXPattern grammar. 3 | """ 4 | 5 | from __future__ import print_function 6 | 7 | import argparse 8 | 9 | from antlr4 import InputStream 10 | import six 11 | 12 | from . import DEFAULT_VERSION 13 | from .exceptions import STIXPatternErrorListener # noqa: F401 14 | from .helpers import brackets_check 15 | from .v20.validator import run_validator as run_validator20 16 | from .v21.validator import run_validator as run_validator21 17 | 18 | 19 | def run_validator(pattern, stix_version=DEFAULT_VERSION): 20 | """ 21 | Validates a pattern against the STIX Pattern grammar. Error messages are 22 | returned in a list. The test passed if the returned list is empty. 23 | """ 24 | if isinstance(pattern, six.string_types): 25 | pattern_str = pattern 26 | pattern = InputStream(pattern) 27 | 28 | else: 29 | pattern_str = pattern.readline() 30 | pattern.seek(0) 31 | 32 | if stix_version == '2.1': 33 | err_messages = run_validator21(pattern) 34 | else: 35 | err_messages = run_validator20(pattern) 36 | 37 | if not brackets_check(pattern_str): 38 | err_messages.insert( 39 | 0, 40 | "FAIL: Error found at line 1:0. input is missing square brackets" 41 | ) 42 | 43 | return err_messages 44 | 45 | 46 | def validate(user_input, stix_version=DEFAULT_VERSION, ret_errs=False, print_errs=False): 47 | """ 48 | Wrapper for run_validator function that returns True if the user_input 49 | contains a valid STIX pattern or False otherwise. The error messages may 50 | also be returned or printed based upon the ret_errs and print_errs arg 51 | values. 52 | """ 53 | 54 | errs = run_validator(user_input, stix_version) 55 | passed = len(errs) == 0 56 | 57 | if print_errs: 58 | for err in errs: 59 | print(err) 60 | 61 | if ret_errs: 62 | return passed, errs 63 | 64 | return passed 65 | 66 | 67 | def main(): 68 | """ 69 | Continues to validate patterns until it encounters EOF within a pattern 70 | file or Ctrl-C is pressed by the user. 71 | """ 72 | parser = argparse.ArgumentParser(description='Validate STIX Patterns.') 73 | parser.add_argument('-f', '--file', 74 | help="Specify this arg to read patterns from a file.", 75 | type=argparse.FileType("r")) 76 | parser.add_argument('-v', '--version', 77 | default=DEFAULT_VERSION, 78 | help="Specify version of STIX 2 specification to validate against.") 79 | args = parser.parse_args() 80 | 81 | pass_count = fail_count = 0 82 | 83 | # I tried using a generator (where each iteration would run raw_input()), 84 | # but raw_input()'s behavior seems to change when called from within a 85 | # generator: I only get one line, then the generator completes! I don't 86 | # know why behavior changes... 87 | import functools 88 | if args.file: 89 | nextpattern = args.file.readline 90 | else: 91 | nextpattern = functools.partial(six.moves.input, "Enter a pattern to validate: ") 92 | 93 | try: 94 | while True: 95 | pattern = nextpattern() 96 | if not pattern: 97 | break 98 | tests_passed, err_strings = validate(pattern, args.version, True) 99 | 100 | if tests_passed: 101 | print("\nPASS: %s" % pattern) 102 | pass_count += 1 103 | 104 | else: 105 | for err in err_strings: 106 | print(err, '\n') 107 | fail_count += 1 108 | except (EOFError, KeyboardInterrupt): 109 | pass 110 | finally: 111 | if args.file: 112 | args.file.close() 113 | 114 | print("\nPASSED:", pass_count, " patterns") 115 | print("FAILED:", fail_count, " patterns") 116 | 117 | 118 | if __name__ == '__main__': 119 | main() 120 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{38,39,310,311,312},packaging,pre-commit-check 3 | 4 | [testenv] 5 | deps = 6 | check-manifest 7 | extras = test 8 | commands = 9 | check-manifest 10 | pytest --cov=stix2patterns stix2patterns/test/ --cov-report term-missing 11 | 12 | passenv = GITHUB_* 13 | 14 | [testenv:packaging] 15 | deps = 16 | twine 17 | setuptools 18 | wheel 19 | commands = 20 | python setup.py sdist bdist_wheel --universal 21 | twine check dist/* 22 | 23 | [testenv:pre-commit-check] 24 | deps = 25 | pre-commit 26 | commands = 27 | pre-commit run --all-files 28 | 29 | [gh-actions] 30 | python = 31 | 3.8: py38 32 | 3.9: py39 33 | 3.10: py310 34 | 3.11: py311 35 | 3.12: py312, packaging, pre-commit-check 36 | --------------------------------------------------------------------------------