├── .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 |
--------------------------------------------------------------------------------