├── .coveragerc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── lint.yml │ └── test-isla.yml ├── .gitignore ├── .gitlab-ci.yml ├── .readthedocs.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── COPYING ├── Dockerfile ├── MANIFEST.in ├── README.md ├── TODO.md ├── evaluations ├── evaluate_csv.py ├── evaluate_rest.py ├── evaluate_scriptsize_c.py ├── evaluate_tar.py ├── evaluate_xml.py ├── optimize_csv_cost_weights.py ├── optimize_rest_cost_weights.py ├── optimize_scriptsize_c_cost_weights.py ├── optimize_tar_cost_weights.py └── optimize_xml_cost_weights.py ├── isla_logo_bright_transparent.png ├── pyproject.toml ├── pytest.ini ├── requirements.txt ├── requirements_dev.txt ├── requirements_test.txt ├── resume_solver.py ├── run_eval.fish ├── run_eval_csv.fish ├── run_eval_rest.fish ├── run_eval_scriptsize_c.fish ├── run_eval_tar.fish ├── run_eval_xml.fish ├── setup.cfg ├── setup.py ├── sphinx ├── .gitignore ├── Makefile ├── _static │ ├── isla.png │ └── style.css ├── conf.py ├── developing │ ├── index.rst │ ├── solver.rst │ └── utilities │ │ ├── helpers.rst │ │ ├── index.rst │ │ └── z3-helpers.rst ├── docutils.conf ├── examples.rst ├── index.rst ├── islaspec.rst ├── make.bat ├── requirements.txt ├── todo.rst └── usage.rst ├── src ├── isla │ ├── IslaLanguage.g4 │ ├── IslaLanguage.tokens │ ├── MexprLexer.g4 │ ├── MexprParser.g4 │ ├── __init__.py │ ├── __main__.py │ ├── bnf.g4 │ ├── bnf.tokens │ ├── bnf │ │ ├── __init__.py │ │ ├── bnf.interp │ │ ├── bnf.tokens │ │ ├── bnfLexer.interp │ │ ├── bnfLexer.py │ │ ├── bnfLexer.tokens │ │ ├── bnfListener.py │ │ └── bnfParser.py │ ├── cli.py │ ├── derivation_tree.py │ ├── evaluator.py │ ├── existential_helpers.py │ ├── fuzzer.py │ ├── global_config.py │ ├── helpers.py │ ├── isla_language │ │ ├── IslaLanguage.interp │ │ ├── IslaLanguage.tokens │ │ ├── IslaLanguageLexer.interp │ │ ├── IslaLanguageLexer.py │ │ ├── IslaLanguageLexer.tokens │ │ ├── IslaLanguageListener.py │ │ ├── IslaLanguageParser.py │ │ └── __init__.py │ ├── isla_predicates.py │ ├── isla_shortcuts.py │ ├── language.py │ ├── mexpr_lexer │ │ ├── MexprLexer.interp │ │ ├── MexprLexer.py │ │ ├── MexprLexer.tokens │ │ └── __init__.py │ ├── mexpr_parser │ │ ├── MexprParser.interp │ │ ├── MexprParser.py │ │ ├── MexprParser.tokens │ │ ├── MexprParserListener.py │ │ └── __init__.py │ ├── mexprlexer.tokens │ ├── mutator.py │ ├── optimizer.py │ ├── parser.py │ ├── performance_evaluator.py │ ├── resources │ │ ├── .islarc │ │ └── cli_stubs │ │ │ ├── README.md │ │ │ ├── constraint.isla │ │ │ ├── grammar.bnf │ │ │ └── grammar.py │ ├── solver.py │ ├── three_valued_truth.py │ ├── trie.py │ ├── type_defs.py │ └── z3_helpers.py └── isla_formalizations │ ├── __init__.py │ ├── csv.py │ ├── rest.py │ ├── scriptsizec.py │ ├── simple_tar.py │ ├── tar.py │ └── xml_lang.py ├── tests ├── .pytest_cache │ ├── .gitignore │ ├── CACHEDIR.TAG │ └── README.md ├── test_cli.py ├── test_concrete_syntax.py ├── test_data.py ├── test_data │ ├── test_simple.tar │ └── test_with_links.tar ├── test_derivation_tree.py ├── test_doctests.py ├── test_evaluator.py ├── test_existential_helpers.py ├── test_formalizations.py ├── test_fuzzer.py ├── test_helpers.py ├── test_language.py ├── test_mutator.py ├── test_parser.py ├── test_path_indexed_trie.py ├── test_predicates.py ├── test_solver.py ├── test_tar_parser.py ├── test_three_valued_truth.py └── xml_demo.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | src/isla/isla_shortcuts.py 4 | src/isla/optimizer.py 5 | src/isla/performance_evaluator.py 6 | src/isla/isla_language/* 7 | src/isla/mexpr_parser/* 8 | src/isla/bnf/* 9 | 10 | [report] 11 | exclude_lines = 12 | @abstractmethod 13 | @abc.abstractmethod -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **System/Installation Specs:** 24 | - ISLa Version: [e.g. 1.8.0] 25 | - Python Version: [e.g. 3.10] 26 | - OS: [e.g. macOS] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | # Trigger the workflow on push or pull request, 5 | # but only for the main branch 6 | push: 7 | branches: 8 | - main 9 | pull_request: 10 | branches: 11 | - main 12 | 13 | jobs: 14 | run-linters: 15 | name: Run linters 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Check out Git repository 20 | uses: actions/checkout@v2 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v1 24 | with: 25 | python-version: "3.10" 26 | 27 | - name: Install Python dependencies 28 | run: pip install black flake8 29 | 30 | - name: Run linters 31 | uses: wearerequired/lint-action@v2 32 | with: 33 | black: true 34 | black_dir: "src/" 35 | black_args: '--extend-exclude="(isla/bnf/*|isla/isla_language/*|isla/mexpr_lexer/*|isla/mexpr_parser/*)"' 36 | flake8: true 37 | flake8_dir: "src/" 38 | flake8_args: "--max-complexity=10 --max-line-length=127 --extend-ignore=E203 --exclude=isla/bnf/,isla/mexpr_parser/,isla/mexpr_lexer/,isla/isla_language/,isla_formalizations/" 39 | -------------------------------------------------------------------------------- /.github/workflows/test-isla.yml: -------------------------------------------------------------------------------- 1 | name: Test ISLa 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Set up Python 3.10 19 | uses: actions/setup-python@v3 20 | with: 21 | python-version: "3.10" 22 | - name: Install dependencies 23 | run: | 24 | sudo apt-get update && sudo apt-get install -y clang python3-dev curl 25 | curl -L https://github.com/Clever/csvlint/releases/download/v0.3.0/csvlint-v0.3.0-linux-amd64.tar.gz | tar xz --strip-components 1 26 | sudo mv csvlint /bin/ 27 | pip install --upgrade pip 28 | pip install -e .[dev,test] 29 | - name: Test with pytest 30 | run: | 31 | python3 -m pytest -n 16 --randomly-seed=0 --html=report.html --self-contained-html --cov-report xml:coverage.xml --cov-report term --cov-config=.coveragerc --cov=isla tests/ 32 | python3 -m coverage lcov 33 | - uses: actions/upload-artifact@v3 34 | with: 35 | name: Test Report 36 | path: report.html 37 | - name: Coveralls 38 | uses: coverallsapp/github-action@master 39 | with: 40 | github-token: ${{ secrets.GITHUB_TOKEN }} 41 | path-to-lcov: coverage.lcov 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__ 3 | prof/ 4 | isla.egg-info/ 5 | .tox/ 6 | dist/ 7 | isla/scriptsizec-ll1.py 8 | isla/testcvc5.py 9 | isla/*.tokens 10 | .eggs/ 11 | gen/ 12 | venv/ 13 | isla_evaluation.sqlite 14 | .coverage 15 | *playground*.py 16 | docker-access-token.txt 17 | isla_evaluation_* 18 | src/isla_solver.egg-info/ 19 | _site/ 20 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # To contribute improvements to CI/CD templates, please follow the Development guide at: 2 | # https://docs.gitlab.com/ee/development/cicd/templates.html 3 | # This specific template is located at: 4 | # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Python.gitlab-ci.yml 5 | 6 | # Official language image. Look for the different tagged releases at: 7 | # https://hub.docker.com/r/library/python/tags/ 8 | image: python:latest 9 | 10 | # Change pip's cache directory to be inside the project directory since we can 11 | # only cache local items. 12 | variables: 13 | PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" 14 | 15 | # Pip's cache doesn't store the python packages 16 | # https://pip.pypa.io/en/stable/reference/pip_install/#caching 17 | # 18 | # If you want to also cache the installed packages, you have to install 19 | # them in a virtualenv and cache it as well. 20 | cache: 21 | paths: 22 | - .cache/pip 23 | - venv/ 24 | 25 | before_script: 26 | - apt update && apt install -y clang python3-dev curl fish parallel 27 | - curl -L https://github.com/Clever/csvlint/releases/download/v0.3.0/csvlint-v0.3.0-linux-amd64.tar.gz | tar xz --strip-components 1 && mv csvlint /bin/ 28 | - csvlint --help # Print out csvlint help for debugging 29 | - python -V # Print out python version for debugging 30 | - pip install --upgrade pip 31 | - pip install virtualenv 32 | - virtualenv venv 33 | - source venv/bin/activate 34 | - pip install -e .[dev,test] 35 | 36 | test: 37 | script: 38 | - python3 -m pytest -n 16 --randomly-seed=0 --html=report.html --self-contained-html --cov-report xml:coverage.xml --cov-report term --cov-config=.coveragerc --cov=isla tests/ 39 | coverage: '/TOTAL *\d+ *\d+ *\d+(\.\d+)?%/' 40 | artifacts: 41 | when: always 42 | paths: 43 | - report.html 44 | - coverage.xml 45 | 46 | build: 47 | script: 48 | - python setup.py bdist_wheel 49 | # an alternative approach is to install and run: 50 | #- pip install dist/* 51 | # run the command here 52 | artifacts: 53 | paths: 54 | - dist/*.whl 55 | - dist/*.tar.gz 56 | 57 | runPerfEval: 58 | script: 59 | - "parallel ::: './run_eval_csv.fish &> perf_result_csv.txt' './run_eval_scriptsize_c.fish &> perf_result_c.txt' './run_eval_rest.fish &> perf_result_rest.txt' './run_eval_tar.fish &> perf_result_tar.txt' './run_eval_xml.fish &> perf_result_xml.txt' &" 60 | - sleep 60 61 | - tail --pid=$! -f perf_result_*.txt 62 | rules: 63 | - if: $CI_PIPELINE_SOURCE == "web" 64 | artifacts: 65 | when: always 66 | paths: 67 | - perf_result_*.txt 68 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.10" 13 | # You can also specify other tool versions: 14 | # nodejs: "19" 15 | # rust: "1.64" 16 | # golang: "1.19" 17 | 18 | # Build documentation in the docs/ directory with Sphinx 19 | sphinx: 20 | configuration: sphinx/conf.py 21 | 22 | # If using Sphinx, optionally build your docs in additional formats such as PDF 23 | # formats: 24 | # - pdf 25 | 26 | # Optionally declare the Python requirements required to build your docs 27 | python: 28 | install: 29 | - requirements: sphinx/requirements.txt 30 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | dominic.steinhoefel@cispa.de. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_CONTAINER=python:3.10.2-alpine3.15 2 | FROM $BASE_CONTAINER 3 | LABEL author="Dominic Steinhoefel" 4 | 5 | USER root 6 | RUN echo "https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories 7 | RUN apk update 8 | RUN apk upgrade 9 | RUN apk add python3-dev git bash fish gcc g++ make cmake clang z3 10 | 11 | RUN wget https://github.com/Clever/csvlint/releases/download/v0.3.0/csvlint-v0.3.0-linux-amd64.tar.gz -O /tmp/csvlint.tar.gz 12 | RUN tar xzf /tmp/csvlint.tar.gz -C /tmp 13 | RUN mv /tmp/csvlint-v0.3.0-linux-amd64/csvlint /usr/bin 14 | 15 | RUN adduser -D isla 16 | USER isla 17 | WORKDIR /home/isla 18 | RUN pip install --upgrade pip wheel 19 | 20 | RUN git clone https://github.com/rindPHI/isla.git 21 | WORKDIR /home/isla/isla 22 | RUN git pull 23 | RUN git checkout v0.10.6 24 | RUN pip install -e .[dev,test] 25 | 26 | WORKDIR /home/isla 27 | CMD ["fish"] 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft src/isla/resources 2 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODOs and Open Issues 2 | 3 | - Test with newer Z3 versions (>= 4.12.1.0), e.g., the heartbleed examples. I experienced 4 | problems with these during a demo preparation. 5 | - Unicode symbols in BNF syntax (e.g., \u0001) 6 | - Add CLI option for `enable-optimized-z3-queries`. 7 | - XPath: Formula `exists assgn: assgn.. = "7"` cannot be parsed 8 | (assignment language). 9 | - Repair too aggressive; example (assignment language): 10 | `isla repair grammar.bnf def-use.isla -i "a := 1 ; c := b"` 11 | - Fix TAR test! Problem introduced in commit with ID 9a82766f76247c1ff0cab738ef7c3133b36e44ce (10.0.9) 12 | Note: Cannot reproduce anymore; but have to look into the TAR case study, it seens to have slowed down. 13 | - Fix test case `test_predicates.test_count_pred_var_as_third_arg` 14 | - Translation of formulas with XPath expressions in `in` qualifiers of quantifiers; see test 15 | case `test_xpath_in_in_expr`. 16 | - Translation of "free nonterminals" together with unnamed bound variables: 17 | Support formulas like `(......) and (exists : ......)` or check 18 | whether this is already supported. But at least in one combination of the 19 | elements of the propositional combination, it probably fails (a free variable 20 | remains). 21 | - Support & test XPath expressions in ISLa predicates. 22 | - Address buggy test case: `test_evaluator.test_addition_with_more_than_two_operands` 23 | [GitHub Issue](https://github.com/rindPHI/isla/issues/2) 24 | -------------------------------------------------------------------------------- /evaluations/evaluate_csv.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from grammar_graph import gg 4 | from grammar_graph.gg import GrammarGraph 5 | 6 | from isla.fuzzer import GrammarFuzzer 7 | from isla.performance_evaluator import Evaluator 8 | from isla.solver import ISLaSolver, CostSettings, CostWeightVector, GrammarBasedBlackboxCostComputer 9 | from isla_formalizations import csv 10 | 11 | max_number_free_instantiations = 10 12 | max_number_smt_instantiations = 5 13 | eval_k = 3 14 | 15 | cost_vector = CostWeightVector( 16 | tree_closing_cost=1, 17 | constraint_cost=0, 18 | derivation_depth_penalty=1, 19 | low_k_coverage_penalty=0, 20 | low_global_k_path_coverage_penalty=0) 21 | 22 | g_colno = lambda timeout: ISLaSolver( 23 | csv.CSV_GRAMMAR, 24 | csv.CSV_COLNO_PROPERTY, 25 | max_number_free_instantiations=max_number_free_instantiations, 26 | max_number_smt_instantiations=max_number_smt_instantiations, 27 | timeout_seconds=timeout, 28 | enforce_unique_trees_in_queue=False, 29 | cost_computer=GrammarBasedBlackboxCostComputer( 30 | CostSettings(cost_vector, k=eval_k), 31 | gg.GrammarGraph.from_grammar(csv.CSV_GRAMMAR)), 32 | global_fuzzer=False, 33 | fuzzer_factory=functools.partial(GrammarFuzzer, min_nonterminals=0, max_nonterminals=30), 34 | ) 35 | 36 | if __name__ == '__main__': 37 | # logging.basicConfig(level=logging.DEBUG) 38 | generators = [csv.CSV_GRAMMAR, g_colno] 39 | jobnames = ["Grammar Fuzzer", "Cnt-Columns"] 40 | 41 | evaluator = Evaluator( 42 | "CSV", 43 | generators, 44 | jobnames, 45 | csv.csv_lint, 46 | GrammarGraph.from_grammar(csv.CSV_GRAMMAR), 47 | default_timeout=60 * 60) 48 | 49 | evaluator.run() 50 | -------------------------------------------------------------------------------- /evaluations/evaluate_rest.py: -------------------------------------------------------------------------------- 1 | from grammar_graph import gg 2 | from grammar_graph.gg import GrammarGraph 3 | 4 | from isla.performance_evaluator import Evaluator 5 | from isla.solver import ISLaSolver, CostSettings, GrammarBasedBlackboxCostComputer, CostWeightVector 6 | from isla_formalizations import rest 7 | 8 | max_number_free_instantiations = 10 9 | max_number_smt_instantiations = 2 10 | eval_k = 4 11 | 12 | cost_vector = CostWeightVector( 13 | tree_closing_cost=7, 14 | constraint_cost=1.5, 15 | derivation_depth_penalty=2.5, 16 | low_k_coverage_penalty=2, 17 | low_global_k_path_coverage_penalty=18) 18 | 19 | cost_computer = GrammarBasedBlackboxCostComputer( 20 | CostSettings(cost_vector, k=eval_k), 21 | gg.GrammarGraph.from_grammar(rest.REST_GRAMMAR), 22 | reset_coverage_after_n_round_with_no_coverage=1500) 23 | 24 | g_link_defuse = lambda timeout: ISLaSolver( 25 | rest.REST_GRAMMAR, 26 | rest.DEF_LINK_TARGETS, 27 | max_number_free_instantiations=max_number_free_instantiations, 28 | max_number_smt_instantiations=max_number_smt_instantiations, 29 | timeout_seconds=timeout, 30 | cost_computer=cost_computer, 31 | ) 32 | 33 | g_link_defuse_len = lambda timeout: ISLaSolver( 34 | rest.REST_GRAMMAR, 35 | rest.DEF_LINK_TARGETS & rest.LENGTH_UNDERLINE, 36 | max_number_free_instantiations=max_number_free_instantiations, 37 | max_number_smt_instantiations=max_number_smt_instantiations, 38 | timeout_seconds=timeout, 39 | cost_computer=cost_computer, 40 | ) 41 | 42 | g_link_defuse_len_numbering = lambda timeout: ISLaSolver( 43 | rest.REST_GRAMMAR, 44 | rest.DEF_LINK_TARGETS & rest.LENGTH_UNDERLINE & rest.LIST_NUMBERING_CONSECUTIVE, 45 | max_number_free_instantiations=max_number_free_instantiations, 46 | max_number_smt_instantiations=max_number_smt_instantiations, 47 | timeout_seconds=timeout, 48 | cost_computer=cost_computer, 49 | ) 50 | 51 | g_link_defuse_len_numbering_no_redef = lambda timeout: ISLaSolver( 52 | rest.REST_GRAMMAR, 53 | rest.DEF_LINK_TARGETS & rest.LENGTH_UNDERLINE & rest.LIST_NUMBERING_CONSECUTIVE & rest.NO_LINK_TARGET_REDEF, 54 | max_number_free_instantiations=max_number_free_instantiations, 55 | max_number_smt_instantiations=max_number_smt_instantiations, 56 | timeout_seconds=timeout, 57 | cost_computer=cost_computer, 58 | ) 59 | 60 | if __name__ == '__main__': 61 | # logging.basicConfig(level=logging.DEBUG) 62 | generators = [ 63 | rest.REST_GRAMMAR, 64 | g_link_defuse, 65 | g_link_defuse_len, 66 | g_link_defuse_len_numbering, 67 | g_link_defuse_len_numbering_no_redef 68 | ] 69 | 70 | jobnames = [ 71 | "Grammar Fuzzer", 72 | "Def-Use", 73 | "Def-Use + Len", 74 | "Def-Use + Len + List Numbering", 75 | "Def-Use + Len + List Numbering + No-Redef", 76 | ] 77 | 78 | evaluator = Evaluator( 79 | "reST", 80 | generators, 81 | jobnames, 82 | rest.render_rst, 83 | GrammarGraph.from_grammar(rest.REST_GRAMMAR), 84 | default_timeout=60 * 60) 85 | 86 | evaluator.run() 87 | -------------------------------------------------------------------------------- /evaluations/evaluate_scriptsize_c.py: -------------------------------------------------------------------------------- 1 | from grammar_graph import gg 2 | from grammar_graph.gg import GrammarGraph 3 | 4 | from isla.performance_evaluator import Evaluator 5 | from isla.solver import ISLaSolver, CostSettings, CostWeightVector, GrammarBasedBlackboxCostComputer 6 | from isla_formalizations import scriptsizec 7 | 8 | max_number_free_instantiations = 10 9 | max_number_smt_instantiations = 2 10 | eval_k = 3 11 | 12 | cost_vector = CostWeightVector( 13 | tree_closing_cost=5, 14 | constraint_cost=2, 15 | derivation_depth_penalty=6, 16 | low_k_coverage_penalty=2, 17 | low_global_k_path_coverage_penalty=21) 18 | 19 | cost_computer = GrammarBasedBlackboxCostComputer( 20 | CostSettings(cost_vector, k=eval_k), 21 | gg.GrammarGraph.from_grammar(scriptsizec.SCRIPTSIZE_C_GRAMMAR), 22 | ) 23 | 24 | g_defuse = lambda timeout: ISLaSolver( 25 | scriptsizec.SCRIPTSIZE_C_GRAMMAR, 26 | scriptsizec.SCRIPTSIZE_C_DEF_USE_CONSTR, 27 | max_number_free_instantiations=max_number_free_instantiations, 28 | max_number_smt_instantiations=max_number_smt_instantiations, 29 | timeout_seconds=timeout, 30 | cost_computer=cost_computer, 31 | ) 32 | 33 | g_redef = lambda timeout: ISLaSolver( 34 | scriptsizec.SCRIPTSIZE_C_GRAMMAR, 35 | scriptsizec.SCRIPTSIZE_C_NO_REDEF_CONSTR, 36 | max_number_free_instantiations=max_number_free_instantiations, 37 | max_number_smt_instantiations=max_number_smt_instantiations, 38 | timeout_seconds=timeout, 39 | cost_computer=cost_computer, 40 | ) 41 | 42 | g_defuse_redef = lambda timeout: ISLaSolver( 43 | scriptsizec.SCRIPTSIZE_C_GRAMMAR, 44 | scriptsizec.SCRIPTSIZE_C_DEF_USE_CONSTR & scriptsizec.SCRIPTSIZE_C_NO_REDEF_CONSTR, 45 | max_number_free_instantiations=max_number_free_instantiations, 46 | max_number_smt_instantiations=max_number_smt_instantiations, 47 | timeout_seconds=timeout, 48 | cost_computer=cost_computer, 49 | ) 50 | 51 | if __name__ == '__main__': 52 | # logging.basicConfig(level=logging.DEBUG) 53 | generators = [scriptsizec.SCRIPTSIZE_C_GRAMMAR, g_defuse, g_redef, g_defuse_redef] 54 | jobnames = ["Grammar Fuzzer", "Def-Use", "No-Redef", "Def-Use + No-Redef"] 55 | 56 | evaluator = Evaluator( 57 | "Scriptsize-C", 58 | generators, 59 | jobnames, 60 | scriptsizec.compile_scriptsizec_clang, 61 | GrammarGraph.from_grammar(scriptsizec.SCRIPTSIZE_C_GRAMMAR), 62 | default_timeout=60 * 60) 63 | 64 | evaluator.run() 65 | -------------------------------------------------------------------------------- /evaluations/evaluate_tar.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from grammar_graph import gg 4 | from grammar_graph.gg import GrammarGraph 5 | 6 | from isla.performance_evaluator import Evaluator 7 | from isla.solver import ISLaSolver, CostSettings, CostWeightVector, GrammarBasedBlackboxCostComputer 8 | from isla_formalizations import tar 9 | 10 | max_number_free_instantiations = 10 11 | max_number_smt_instantiations = 2 12 | eval_k = 4 13 | 14 | cost_vector = CostWeightVector( 15 | tree_closing_cost=3, 16 | constraint_cost=0, 17 | derivation_depth_penalty=2, 18 | low_k_coverage_penalty=0, 19 | low_global_k_path_coverage_penalty=0) 20 | 21 | length_constraints = ( 22 | # tar.link_constraint & 23 | tar.file_name_length_constraint & 24 | tar.file_mode_length_constraint & 25 | tar.uid_length_constraint & 26 | tar.gid_length_constraint & 27 | tar.file_size_constr & 28 | tar.mod_time_length_constraint & 29 | tar.linked_file_name_length_constraint & 30 | tar.uname_length_constraint & 31 | tar.gname_length_constraint & 32 | tar.dev_maj_num_length_constraint & 33 | tar.dev_min_num_length_constraint & 34 | tar.prefix_length_constraint & 35 | tar.header_padding_length_constraint & 36 | tar.content_length_constraint & 37 | # tar.checksum_constraint & 38 | tar.content_size_constr & 39 | tar.final_entry_length_constraint 40 | ) 41 | 42 | length_constraints_and_checksum = ( 43 | # tar.link_constraint & 44 | tar.file_name_length_constraint & 45 | tar.file_mode_length_constraint & 46 | tar.uid_length_constraint & 47 | tar.gid_length_constraint & 48 | tar.file_size_constr & 49 | tar.mod_time_length_constraint & 50 | tar.linked_file_name_length_constraint & 51 | tar.uname_length_constraint & 52 | tar.gname_length_constraint & 53 | tar.dev_maj_num_length_constraint & 54 | tar.dev_min_num_length_constraint & 55 | tar.prefix_length_constraint & 56 | tar.header_padding_length_constraint & 57 | tar.content_length_constraint & 58 | tar.checksum_constraint & 59 | tar.content_size_constr & 60 | tar.final_entry_length_constraint 61 | ) 62 | 63 | length_constraints_and_checksum_and_link = ( 64 | tar.link_constraint & 65 | tar.file_name_length_constraint & 66 | tar.file_mode_length_constraint & 67 | tar.uid_length_constraint & 68 | tar.gid_length_constraint & 69 | tar.file_size_constr & 70 | tar.mod_time_length_constraint & 71 | tar.linked_file_name_length_constraint & 72 | tar.uname_length_constraint & 73 | tar.gname_length_constraint & 74 | tar.dev_maj_num_length_constraint & 75 | tar.dev_min_num_length_constraint & 76 | tar.prefix_length_constraint & 77 | tar.header_padding_length_constraint & 78 | tar.content_length_constraint & 79 | tar.checksum_constraint & 80 | tar.content_size_constr & 81 | tar.final_entry_length_constraint 82 | ) 83 | 84 | g_len = lambda timeout: ISLaSolver( 85 | tar.TAR_GRAMMAR, 86 | length_constraints, 87 | max_number_free_instantiations=max_number_free_instantiations, 88 | max_number_smt_instantiations=max_number_smt_instantiations, 89 | timeout_seconds=timeout, 90 | cost_computer=GrammarBasedBlackboxCostComputer( 91 | CostSettings(cost_vector, k=eval_k), 92 | gg.GrammarGraph.from_grammar(tar.TAR_GRAMMAR)), 93 | ) 94 | 95 | g_len_cs = lambda timeout: ISLaSolver( 96 | tar.TAR_GRAMMAR, 97 | length_constraints_and_checksum, 98 | max_number_free_instantiations=max_number_free_instantiations, 99 | max_number_smt_instantiations=max_number_smt_instantiations, 100 | timeout_seconds=timeout, 101 | cost_computer=GrammarBasedBlackboxCostComputer( 102 | CostSettings(cost_vector, k=eval_k), 103 | gg.GrammarGraph.from_grammar(tar.TAR_GRAMMAR)), 104 | ) 105 | 106 | g_len_cs_lin = lambda timeout: ISLaSolver( 107 | tar.TAR_GRAMMAR, 108 | length_constraints_and_checksum_and_link, 109 | max_number_free_instantiations=max_number_free_instantiations, 110 | max_number_smt_instantiations=max_number_smt_instantiations, 111 | timeout_seconds=timeout, 112 | cost_computer=GrammarBasedBlackboxCostComputer( 113 | CostSettings(cost_vector, k=eval_k), 114 | gg.GrammarGraph.from_grammar(tar.TAR_GRAMMAR)), 115 | ) 116 | 117 | if __name__ == '__main__': 118 | sys.setrecursionlimit(5000) 119 | 120 | # logging.basicConfig(level=logging.DEBUG) 121 | generators = [tar.TAR_GRAMMAR, g_len, g_len_cs, g_len_cs_lin] 122 | jobnames = ["Grammar Fuzzer", "Length", "Length + Checksum", "Length + Checksum + Def-Use"] 123 | 124 | evaluator = Evaluator( 125 | "tar", 126 | generators, 127 | jobnames, 128 | tar.extract_tar, 129 | GrammarGraph.from_grammar(tar.TAR_GRAMMAR), 130 | default_timeout=60 * 60) 131 | 132 | evaluator.run() 133 | -------------------------------------------------------------------------------- /evaluations/evaluate_xml.py: -------------------------------------------------------------------------------- 1 | from grammar_graph import gg 2 | from grammar_graph.gg import GrammarGraph 3 | 4 | from isla.performance_evaluator import Evaluator 5 | from isla.solver import ISLaSolver, CostSettings, CostWeightVector, GrammarBasedBlackboxCostComputer 6 | from isla_formalizations import xml_lang 7 | 8 | max_number_free_instantiations = 10 9 | max_number_smt_instantiations = 2 10 | eval_k = 4 11 | 12 | cost_vector = CostWeightVector( 13 | tree_closing_cost=10, 14 | constraint_cost=0, 15 | derivation_depth_penalty=6, 16 | low_k_coverage_penalty=0, 17 | low_global_k_path_coverage_penalty=13) 18 | 19 | g_wf = lambda timeout: ISLaSolver( 20 | xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 21 | xml_lang.XML_WELLFORMEDNESS_CONSTRAINT, 22 | max_number_free_instantiations=max_number_free_instantiations, 23 | max_number_smt_instantiations=max_number_smt_instantiations, 24 | timeout_seconds=timeout, 25 | cost_computer=GrammarBasedBlackboxCostComputer( 26 | CostSettings(cost_vector, k=eval_k), 27 | gg.GrammarGraph.from_grammar(xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES)), 28 | ) 29 | 30 | g_ns = lambda timeout: ISLaSolver( 31 | xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 32 | xml_lang.XML_NAMESPACE_CONSTRAINT, 33 | max_number_free_instantiations=max_number_free_instantiations, 34 | max_number_smt_instantiations=max_number_smt_instantiations, 35 | timeout_seconds=timeout, 36 | cost_computer=GrammarBasedBlackboxCostComputer( 37 | CostSettings(cost_vector, k=eval_k), 38 | gg.GrammarGraph.from_grammar(xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES)), 39 | ) 40 | 41 | g_redef = lambda timeout: ISLaSolver( 42 | xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 43 | xml_lang.XML_NO_ATTR_REDEF_CONSTRAINT, 44 | max_number_free_instantiations=max_number_free_instantiations, 45 | max_number_smt_instantiations=max_number_smt_instantiations, 46 | timeout_seconds=timeout, 47 | cost_computer=GrammarBasedBlackboxCostComputer( 48 | CostSettings(cost_vector, k=eval_k), 49 | gg.GrammarGraph.from_grammar(xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES)), 50 | ) 51 | 52 | g_wf_ns = lambda timeout: ISLaSolver( 53 | xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 54 | xml_lang.XML_WELLFORMEDNESS_CONSTRAINT & xml_lang.XML_NAMESPACE_CONSTRAINT, 55 | max_number_free_instantiations=max_number_free_instantiations, 56 | max_number_smt_instantiations=max_number_smt_instantiations, 57 | timeout_seconds=timeout, 58 | cost_computer=GrammarBasedBlackboxCostComputer( 59 | CostSettings(cost_vector, k=eval_k), 60 | gg.GrammarGraph.from_grammar(xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES)), 61 | ) 62 | 63 | g_wf_ns_redef = lambda timeout: ISLaSolver( 64 | xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 65 | xml_lang.XML_WELLFORMEDNESS_CONSTRAINT & 66 | xml_lang.XML_NAMESPACE_CONSTRAINT & 67 | xml_lang.XML_NO_ATTR_REDEF_CONSTRAINT, 68 | max_number_free_instantiations=max_number_free_instantiations, 69 | max_number_smt_instantiations=max_number_smt_instantiations, 70 | timeout_seconds=timeout, 71 | cost_computer=GrammarBasedBlackboxCostComputer( 72 | CostSettings(cost_vector, k=eval_k), 73 | gg.GrammarGraph.from_grammar(xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES)), 74 | ) 75 | 76 | if __name__ == '__main__': 77 | # logging.basicConfig(level=logging.DEBUG) 78 | generators = [xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, g_wf, g_ns, g_redef, g_wf_ns, g_wf_ns_redef] 79 | jobnames = ["Grammar Fuzzer", "Balance", "Def-Use", "No-Redef", "Balance + Def-Use", "Balance + Def-Use + No-Redef"] 80 | 81 | evaluator = Evaluator( 82 | "XML", 83 | generators, 84 | jobnames, 85 | xml_lang.validate_xml, 86 | GrammarGraph.from_grammar(xml_lang.XML_GRAMMAR_WITH_NAMESPACE_PREFIXES), 87 | default_timeout=60 * 60) 88 | 89 | evaluator.run() 90 | -------------------------------------------------------------------------------- /evaluations/optimize_csv_cost_weights.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import random 3 | 4 | from isla.derivation_tree import DerivationTree 5 | from isla.optimizer import auto_tune_weight_vector 6 | from isla_formalizations import csv 7 | 8 | 9 | def validator(t: DerivationTree) -> bool: 10 | return csv.csv_lint(t) is True 11 | 12 | 13 | if __name__ == '__main__': 14 | logging.basicConfig(level=logging.ERROR) 15 | # logging.getLogger("evaluator").setLevel(logging.DEBUG) 16 | 17 | random.seed(3519871684213) 18 | 19 | tune_result = auto_tune_weight_vector( 20 | csv.CSV_GRAMMAR, 21 | csv.CSV_COLNO_PROPERTY, 22 | validator, 23 | timeout=60, 24 | population_size=32, 25 | generations=5, 26 | cpu_count=32 27 | ) 28 | 29 | print(tune_result[1]) 30 | -------------------------------------------------------------------------------- /evaluations/optimize_rest_cost_weights.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import random 3 | import sys 4 | 5 | from isla.derivation_tree import DerivationTree 6 | from isla.optimizer import auto_tune_weight_vector, evaluate_mutated_cost_vectors, mutate_cost_vector 7 | from isla.solver import CostWeightVector 8 | from isla_formalizations import rest 9 | 10 | 11 | def validator(t: DerivationTree) -> bool: 12 | return rest.render_rst(t) is True 13 | 14 | 15 | if __name__ == '__main__': 16 | # logging.basicConfig(level=logging.ERROR) 17 | logging.getLogger("evaluator").setLevel(logging.INFO) 18 | 19 | random.seed(123456) 20 | 21 | start_vector = CostWeightVector( 22 | tree_closing_cost=7, 23 | constraint_cost=1.5, 24 | derivation_depth_penalty=4, 25 | low_k_coverage_penalty=2, 26 | low_global_k_path_coverage_penalty=21) 27 | 28 | tune_result = auto_tune_weight_vector( 29 | rest.REST_GRAMMAR, 30 | rest.DEF_LINK_TARGETS & rest.LENGTH_UNDERLINE & rest.LIST_NUMBERING_CONSECUTIVE & rest.NO_LINK_TARGET_REDEF, 31 | validator, 32 | timeout=60, 33 | population_size=16, 34 | generations=6, 35 | cpu_count=16, 36 | k=4, 37 | seed_population=[ 38 | mutate_cost_vector(start_vector) 39 | for _ in range(15) 40 | ] + [start_vector] 41 | ) 42 | 43 | print(tune_result[1]) 44 | -------------------------------------------------------------------------------- /evaluations/optimize_scriptsize_c_cost_weights.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import random 3 | 4 | from isla.derivation_tree import DerivationTree 5 | from isla.optimizer import auto_tune_weight_vector 6 | from isla.solver import CostWeightVector 7 | from isla_formalizations.scriptsizec import SCRIPTSIZE_C_GRAMMAR, compile_scriptsizec_clang, \ 8 | SCRIPTSIZE_C_DEF_USE_CONSTR, SCRIPTSIZE_C_NO_REDEF_CONSTR 9 | 10 | 11 | def validator(t: DerivationTree) -> bool: 12 | return compile_scriptsizec_clang(t) is True 13 | 14 | 15 | if __name__ == '__main__': 16 | logging.basicConfig(level=logging.ERROR) 17 | # logging.getLogger("evaluator").setLevel(logging.DEBUG) 18 | 19 | random.seed(5416874352164316) 20 | 21 | tune_result = auto_tune_weight_vector( 22 | SCRIPTSIZE_C_GRAMMAR, 23 | SCRIPTSIZE_C_DEF_USE_CONSTR & SCRIPTSIZE_C_NO_REDEF_CONSTR, 24 | validator, 25 | timeout=60, 26 | population_size=16, 27 | generations=5, 28 | cpu_count=16, 29 | seed_population=[ 30 | CostWeightVector( 31 | tree_closing_cost=4.2, 32 | constraint_cost=0, 33 | derivation_depth_penalty=7, 34 | low_k_coverage_penalty=10, 35 | low_global_k_path_coverage_penalty=20, 36 | )]) 37 | 38 | tune_result[0].plot("/tmp/scriptsize_c_autotune_result_state.pdf", "Scriptsize-C Auto-Tune Result Config Stats") 39 | -------------------------------------------------------------------------------- /evaluations/optimize_tar_cost_weights.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import random 3 | 4 | from isla.derivation_tree import DerivationTree 5 | from isla.optimizer import auto_tune_weight_vector 6 | from isla.solver import CostWeightVector 7 | from isla_formalizations import tar 8 | from isla_formalizations.scriptsizec import SCRIPTSIZE_C_GRAMMAR, compile_scriptsizec_clang, \ 9 | SCRIPTSIZE_C_DEF_USE_CONSTR, SCRIPTSIZE_C_NO_REDEF_CONSTR 10 | from isla_formalizations.tar import TAR_GRAMMAR 11 | 12 | 13 | def validator(t: DerivationTree) -> bool: 14 | return tar.extract_tar(t) is True 15 | 16 | 17 | if __name__ == '__main__': 18 | logging.basicConfig(level=logging.ERROR) 19 | # logging.getLogger("evaluator").setLevel(logging.DEBUG) 20 | 21 | random.seed(5416874352164316) 22 | 23 | length_constraints_and_checksum_and_link = ( 24 | tar.link_constraint & 25 | tar.file_name_length_constraint & 26 | tar.file_mode_length_constraint & 27 | tar.uid_length_constraint & 28 | tar.gid_length_constraint & 29 | tar.file_size_constr & 30 | tar.mod_time_length_constraint & 31 | tar.linked_file_name_length_constraint & 32 | tar.uname_length_constraint & 33 | tar.gname_length_constraint & 34 | tar.dev_maj_num_length_constraint & 35 | tar.dev_min_num_length_constraint & 36 | tar.prefix_length_constraint & 37 | tar.header_padding_length_constraint & 38 | tar.content_length_constraint & 39 | tar.checksum_constraint & 40 | tar.content_size_constr & 41 | tar.final_entry_length_constraint) 42 | 43 | tune_result = auto_tune_weight_vector( 44 | TAR_GRAMMAR, 45 | length_constraints_and_checksum_and_link, 46 | validator, 47 | timeout=60, 48 | population_size=16, 49 | generations=5, 50 | cpu_count=16, 51 | seed_population=[ 52 | CostWeightVector( 53 | tree_closing_cost=4.2, 54 | constraint_cost=0, 55 | derivation_depth_penalty=7, 56 | low_k_coverage_penalty=10, 57 | low_global_k_path_coverage_penalty=20, 58 | )]) 59 | 60 | tune_result[0].plot("/tmp/tar_autotune_result_state.pdf", "TAR Auto-Tune Result Config Stats") 61 | -------------------------------------------------------------------------------- /evaluations/optimize_xml_cost_weights.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import random 3 | import xml.etree.ElementTree as ET 4 | 5 | from isla.derivation_tree import DerivationTree 6 | from isla.optimizer import auto_tune_weight_vector, mutate_cost_vector 7 | from isla.solver import CostWeightVector 8 | from isla_formalizations.xml_lang import XML_NAMESPACE_CONSTRAINT, XML_WELLFORMEDNESS_CONSTRAINT, \ 9 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, XML_NO_ATTR_REDEF_CONSTRAINT 10 | 11 | 12 | def validate_xml(inp: DerivationTree) -> bool: 13 | try: 14 | ET.fromstring(str(inp)) 15 | return True 16 | except ET.ParseError: 17 | return False 18 | 19 | 20 | if __name__ == '__main__': 21 | logging.basicConfig(level=logging.ERROR) 22 | logging.getLogger("evaluator").setLevel(logging.INFO) 23 | 24 | random.seed(13468432149) 25 | 26 | start_vector = CostWeightVector( 27 | tree_closing_cost=10, 28 | constraint_cost=0, 29 | derivation_depth_penalty=6, 30 | low_k_coverage_penalty=0, 31 | low_global_k_path_coverage_penalty=18) 32 | 33 | tune_result = auto_tune_weight_vector( 34 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 35 | XML_WELLFORMEDNESS_CONSTRAINT & XML_NAMESPACE_CONSTRAINT & XML_NO_ATTR_REDEF_CONSTRAINT, 36 | validator=validate_xml, 37 | timeout=60, 38 | population_size=16, 39 | generations=6, 40 | cpu_count=16, 41 | k=4, 42 | seed_population=[start_vector] + [ 43 | mutate_cost_vector(start_vector) 44 | for _ in range(15) 45 | ] 46 | ) 47 | 48 | print(f"Optimal cost vector: {tune_result[1]}") 49 | -------------------------------------------------------------------------------- /isla_logo_bright_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rindPHI/isla/1a04b7833d2960cffe037fb88826111769fefbeb/isla_logo_bright_transparent.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "setuptools-antlr", 5 | "wheel" 6 | ] 7 | build-backend = "setuptools.build_meta" 8 | 9 | [project] 10 | name = "isla-solver" 11 | authors = [ 12 | { name = "Dominic Steinhoefel", email = "dominic.steinhoefel@cispa.de" }, 13 | ] 14 | description = "The ISLa Input Specification Language and its solver." 15 | license = { file = "COPYING" } 16 | requires-python = ">=3.10" 17 | classifiers = [ 18 | "Intended Audience :: Science/Research", 19 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", 20 | "Programming Language :: Python :: 3.10", 21 | "Operating System :: OS Independent", 22 | "Topic :: Scientific/Engineering", 23 | "Topic :: Security", 24 | "Topic :: Software Development :: Testing", 25 | ] 26 | dynamic = ["version", "readme"] 27 | 28 | dependencies = [ 29 | "antlr4-python3-runtime>=4.13", 30 | "datrie>=0.8.2", 31 | "grammar_to_regex>=0.0.4", 32 | "grammar_graph>=0.1.15", 33 | "ijson>=3.1.4", 34 | "packaging>=21.3", 35 | "pathos>=0.2.9", 36 | "proxyorderedset>=0.3.5", 37 | "returns>=0.21.0", 38 | "toml>=0.10.2", 39 | "wheel>=0.37.1", 40 | "z3-solver>=4.8.17.0,<=4.11.2.0", 41 | ] 42 | 43 | [project.urls] 44 | "Homepage" = "https://github.com/rindPHI/isla/" 45 | "Bug Tracker" = "https://github.com/rindPHI/isla/issues" 46 | 47 | [project.optional-dependencies] 48 | test = [ 49 | "docutils>=0.18.1", 50 | "matplotlib>=3.5.3", 51 | "pytest-cov>=3.0.0", 52 | "pytest-html>=3.1.1", 53 | "pytest-profiling>=1.7.0", 54 | "pytest-pycharm>=0.7.0", 55 | "pytest-randomly>=3.12.0", 56 | "pytest-rerunfailures>=10.2", 57 | "pytest-xdist>=2.4.0", 58 | "pytest>=7.1.2", 59 | "tox>=3.25.0", 60 | ] 61 | dev = [ 62 | "black>=22.8.0", 63 | "build>=0.8.0", 64 | "flake8>=5.0.4", 65 | "setuptools-antlr>=0.4.0", 66 | "sphinx>=6.1.3", 67 | "sphinx-book-theme>=1.0.0", 68 | "twine>=4.0.1", 69 | ] 70 | 71 | [project.scripts] 72 | isla = "isla.cli:main" 73 | 74 | [tool.setuptools] 75 | include-package-data = true # `true` is the default value 76 | 77 | [tool.setuptools.dynamic] 78 | version = {attr = "isla.__version__"} 79 | readme = {file = ["README.md"], content-type = "text/markdown"} 80 | 81 | [tool.black] 82 | line-length = 88 83 | target-version = ['py310'] 84 | extend-exclude = ''' 85 | ( 86 | ^/src/isla/bnf/* 87 | | ^/src/isla/isla_language/* 88 | | ^/src/isla/mexpr_lexer/* 89 | | ^/src/isla/mexpr_parser/* 90 | ) 91 | ''' 92 | 93 | [tool.mypy] 94 | plugins = ["returns.contrib.mypy.returns_plugin"] 95 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | 3 | log_cli = 1 4 | log_cli_level = INFO 5 | log_cli_format = %(asctime)s [%(levelname)8s] (%(name)s) %(message)s (%(filename)s:%(lineno)s) 6 | log_cli_date_format=%Y-%m-%d %H:%M:%S 7 | 8 | ;log_file = /tmp/isla_log.txt 9 | ;log_file_level = DEBUG 10 | ;log_file_format = %(asctime)s [%(levelname)8s] (%(name)s) %(message)s (%(filename)s:%(lineno)s) 11 | ;log_file_date_format=%Y-%m-%d %H:%M:%S 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | antlr4-python3-runtime>=4.13 2 | datrie>=0.8.2 3 | grammar_graph>=0.1.15 4 | grammar_to_regex>=0.0.4 5 | ijson>=3.1.4 6 | packaging>=21.3 7 | pathos>=0.2.9 8 | proxyorderedset>=0.3.5 9 | returns>=0.21.0 10 | setuptools-antlr>=0.4.0 11 | toml>=0.10.2 12 | wheel>=0.37.1 13 | z3-solver>=4.8.17.0,<=4.11.2.0 14 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | antlr4-python3-runtime>=4.13 2 | black>=22.8.0 3 | build>=0.8.0 4 | datrie>=0.8.2 5 | docutils>=0.18.1 6 | flake8>=5.0.4 7 | grammar_graph>=0.1.15 8 | grammar_to_regex>=0.0.4 9 | ijson>=3.1.4 10 | matplotlib>=3.5.3 11 | packaging>=21.3 12 | pathos>=0.2.9 13 | proxyorderedset>=0.3.5 14 | pytest-cov>=3.0.0 15 | pytest-html>=3.1.1 16 | pytest-profiling>=1.7.0 17 | pytest-pycharm>=0.7.0 18 | pytest-randomly>=3.12.0 19 | pytest-rerunfailures>=10.2 20 | pytest-xdist>=2.4.0 21 | pytest>=7.1.2 22 | returns>=0.21.0 23 | setuptools-antlr>=0.4.0 24 | sphinx-book-theme>=1.0.0 25 | sphinx>=6.1.3 26 | toml>=0.10.2 27 | tox>=3.25.0 28 | twine>=4.0.1 29 | wheel>=0.37.1 30 | z3-solver>=4.8.17.0,<=4.11.2.0 31 | -------------------------------------------------------------------------------- /requirements_test.txt: -------------------------------------------------------------------------------- 1 | antlr4-python3-runtime>=4.13 2 | datrie>=0.8.2 3 | docutils>=0.18.1 4 | grammar_graph>=0.1.15 5 | grammar_to_regex>=0.0.4 6 | ijson>=3.1.4 7 | matplotlib>=3.5.3 8 | packaging>=21.3 9 | pathos>=0.2.9 10 | proxyorderedset>=0.3.5 11 | pytest-cov>=3.0.0 12 | pytest-html>=3.1.1 13 | pytest-profiling>=1.7.0 14 | pytest-pycharm>=0.7.0 15 | pytest-randomly>=3.12.0 16 | pytest-rerunfailures>=10.2 17 | pytest-xdist>=2.4.0 18 | pytest>=7.1.2 19 | returns>=0.21.0 20 | setuptools-antlr>=0.4.0 21 | toml>=0.10.2 22 | tox>=3.25.0 23 | z3-solver>=4.8.17.0,<=4.11.2.0 24 | -------------------------------------------------------------------------------- /resume_solver.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | 3 | from isla.solver import ISLaSolver 4 | 5 | with (open("/tmp/saved_debug_state", "rb")) as debug_state_file: 6 | try: 7 | solver: ISLaSolver = pickle.load(debug_state_file) 8 | while True: 9 | try: 10 | result = solver.solve() 11 | print(f"Found solution: {result}") 12 | except StopIteration: 13 | pass 14 | except EOFError as e: 15 | print(e) 16 | -------------------------------------------------------------------------------- /run_eval.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | set script (basename (status -f)) 4 | argparse --name="$script" 's/subject=' 't/seconds=' 'r/runs=' 'j/jobs=' 'd/database=' 'h/help' -- $argv or return 5 | 6 | if set -q _flag_h 7 | echo "Evaluate the performance of the ISLa solver." 8 | echo 9 | echo "$script [-h] [-s SUBJECT] [-t SECONDS] [-r RUNS] [-j JOBS]" 10 | echo 11 | echo "OPTIONS" 12 | echo "The following $script options are available." 13 | echo 14 | echo "-h or --help" 15 | echo " Show this help message." 16 | echo 17 | echo "-d or --database" 18 | echo " The path to the sqlite database where the results should be stored." 19 | echo 20 | echo "-j or --jobs" 21 | echo " A comma-separated list of jobs that should be executed." 22 | echo 23 | echo "-r or --runs" 24 | echo " The number of times each job should be run." 25 | echo 26 | echo "-t or --seconds" 27 | echo " The number of seconds each individual job execution should run." 28 | echo 29 | echo "-s or --subject" 30 | echo " The name of the subject to evaluate. This has to be one of (without the quotes):" 31 | echo 32 | echo " - 'csv'" 33 | echo " - 'rest'" 34 | echo " - 'scriptsize_c'" 35 | echo " - 'tar'" 36 | echo " - 'xml'" 37 | exit 38 | end 39 | 40 | if set -q _flag_s 41 | set subject $_flag_s 42 | else 43 | echo "You did not choose the subject to evaluate. I'm choosing 'scriptsize_c' for you." 44 | echo "Call '$script -h' for help." 45 | set subject "scriptsize_c" 46 | end 47 | 48 | set secs 3600 49 | if set -q _flag_t 50 | set secs $_flag_t 51 | end 52 | 53 | set runs 2 54 | if set -q _flag_r 55 | set runs $_flag_r 56 | end 57 | 58 | set curr_dir (pwd) 59 | set db "$curr_dir/isla_evaluation_$subject.sqlite" 60 | if set -q _flag_d 61 | set db $_flag_d 62 | end 63 | 64 | echo "Database: $db" 65 | 66 | if set -q _flag_j 67 | set jobs (string split "," "$_flag_j") 68 | else 69 | switch $subject 70 | case csv 71 | set jobs "Grammar Fuzzer" "Cnt-Columns" 72 | case rest 73 | set jobs "Grammar Fuzzer" "Def-Use" "Def-Use + Len" "Def-Use + Len + List Numbering" "Def-Use + Len + List Numbering + No-Redef" 74 | case scriptsize_c 75 | set jobs "Grammar Fuzzer" "Def-Use" "No-Redef" "Def-Use + No-Redef" 76 | case tar 77 | set jobs "Grammar Fuzzer" "Length" "Length + Checksum" "Length + Checksum + Def-Use" 78 | case xml 79 | set jobs "Grammar Fuzzer" "Def-Use" "Balance" "Balance + Def-Use" "Balance + Def-Use + No-Redef" 80 | case '*' 81 | echo "Unknown subject $subject" 82 | echo "Call '$script -h' for help." 83 | exit 1 84 | end 85 | end 86 | 87 | if test -z "$VIRTUAL_ENV" -a -d venv 88 | source venv/bin/activate.fish 89 | end 90 | 91 | echo "Jobs: "(string join ", " $jobs) 92 | echo "Running each job $runs times for $secs seconds." 93 | 94 | set -x PYTHONPATH (pwd) 95 | set -x PYTHONHASHSEED 0 96 | 97 | # Scriptsize-C 98 | for j in $jobs 99 | for n in (seq $runs) 100 | python3 -u -O evaluations/evaluate_$subject.py -g -t $secs -j "$j" --db "$db" 101 | end 102 | end 103 | 104 | set jobargs (string join "," $jobs) 105 | python3 -u -O evaluations/evaluate_$subject.py -v -p -a -n -1 --db "$db" -j "$jobargs" 106 | 107 | -------------------------------------------------------------------------------- /run_eval_csv.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | ./run_eval.fish -s "csv" $argv 4 | -------------------------------------------------------------------------------- /run_eval_rest.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | ./run_eval.fish -s "rest" $argv 4 | -------------------------------------------------------------------------------- /run_eval_scriptsize_c.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | ./run_eval.fish -s "scriptsize_c" $argv 4 | -------------------------------------------------------------------------------- /run_eval_tar.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | ./run_eval.fish -s "tar" $argv 4 | -------------------------------------------------------------------------------- /run_eval_xml.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | ./run_eval.fish -s "xml" $argv 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [antlr] 2 | # Specify grammars to generate parsers for; default: None 3 | #grammars = [grammar> ...] 4 | # Specify directories where output is generated; default: ./ 5 | #output = [default=] 6 | # [= ...] 7 | output = default=. 8 | # Generate a parse tree listener (yes|no); default: yes 9 | listener = yes 10 | # Generate parse tree visitor (yes|no); default: no 11 | #visitor = no 12 | # Generate file dependencies (yes|no); default: no 13 | #depend = no 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | if __name__ == "__main__": 4 | setup( 5 | setup_requires=["setuptools-antlr"] 6 | ) 7 | -------------------------------------------------------------------------------- /sphinx/.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | -------------------------------------------------------------------------------- /sphinx/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 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 | -------------------------------------------------------------------------------- /sphinx/_static/isla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rindPHI/isla/1a04b7833d2960cffe037fb88826111769fefbeb/sphinx/_static/isla.png -------------------------------------------------------------------------------- /sphinx/_static/style.css: -------------------------------------------------------------------------------- 1 | .bd-main { 2 | flex-grow: 1; 3 | } -------------------------------------------------------------------------------- /sphinx/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # For the full list of built-in configuration values, see the documentation: 4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 5 | 6 | # -- Project information ----------------------------------------------------- 7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 8 | 9 | project = 'ISLa' 10 | copyright = '2023, Dominic Steinhöfel' 11 | author = 'Dominic Steinhöfel' 12 | release = '1.11.0' 13 | 14 | # -- General configuration --------------------------------------------------- 15 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 16 | 17 | extensions = [ 18 | 'sphinx.ext.autodoc', 19 | 'sphinx.ext.viewcode', 20 | ] 21 | 22 | templates_path = ['_templates'] 23 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 24 | 25 | # -- Autodoc ----------------------------------------------------------------- 26 | # autodoc_default_options = { 27 | # 'members': True, 28 | # } 29 | autodoc_typehints = "description" 30 | autodoc_class_signature = "separated" 31 | autosummary_generate = True 32 | 33 | # -- Options for HTML output ------------------------------------------------- 34 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 35 | 36 | html_theme = 'sphinx_book_theme' 37 | html_static_path = ["_static"] 38 | html_css_files = ["style.css"] 39 | html_logo = "_static/isla.png" 40 | -------------------------------------------------------------------------------- /sphinx/developing/index.rst: -------------------------------------------------------------------------------- 1 | Developer's Guide to ISLa 2 | ========================= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Contents: 7 | 8 | solver 9 | utilities/index 10 | 11 | .. note:: 12 | This page is under construction. 13 | -------------------------------------------------------------------------------- /sphinx/developing/solver.rst: -------------------------------------------------------------------------------- 1 | The "solver" Module 2 | =================== 3 | 4 | .. note:: 5 | This page is under construction. 6 | 7 | The ISLaSolver Class 8 | -------------------- 9 | 10 | .. autoclass:: isla.solver.ISLaSolver 11 | 12 | Main Functions 13 | ^^^^^^^^^^^^^^ 14 | 15 | .. automethod:: isla.solver.ISLaSolver.solve 16 | 17 | Below, we show the source code of :meth:`~isla.solver.ISLaSolver.solve()`. The code contains sufficient inline documentation. Essentially, it realizes a transition system between *Constrained Derivation Trees,* i.e., pairs of constraints and (potentially open) derivation trees. In the implementation, these structures are called *states.* The solver takes the state on top of a queue (a Fibonacci heap) and processes it with the first applicable "transition rule," called "elimination function" in the code. The function loops until at least one solution has been found, and only as long as there are still elements in the queue. 18 | 19 | .. literalinclude:: ../../src/isla/solver.py 20 | :pyobject: ISLaSolver.solve 21 | :start-at: if self.timeout_seconds 22 | :dedent: 8 23 | 24 | .. automethod:: isla.solver.ISLaSolver.check 25 | 26 | .. automethod:: isla.solver.ISLaSolver.parse 27 | 28 | .. automethod:: isla.solver.ISLaSolver.repair 29 | 30 | .. automethod:: isla.solver.ISLaSolver.mutate 31 | -------------------------------------------------------------------------------- /sphinx/developing/utilities/helpers.rst: -------------------------------------------------------------------------------- 1 | The "helpers" Module 2 | ==================== 3 | 4 | .. note:: 5 | This page is under construction. 6 | -------------------------------------------------------------------------------- /sphinx/developing/utilities/index.rst: -------------------------------------------------------------------------------- 1 | Utility Functions and Classes 2 | ============================= 3 | 4 | .. note:: 5 | This page is under construction. 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | :caption: Contents: 10 | 11 | helpers 12 | z3-helpers 13 | -------------------------------------------------------------------------------- /sphinx/developing/utilities/z3-helpers.rst: -------------------------------------------------------------------------------- 1 | The "z3-helpers" Module 2 | ======================= 3 | 4 | .. note:: 5 | This page is under construction. 6 | -------------------------------------------------------------------------------- /sphinx/docutils.conf: -------------------------------------------------------------------------------- 1 | [restructuredtext parser] 2 | syntax_highlight = short 3 | -------------------------------------------------------------------------------- /sphinx/examples.rst: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | .. note:: 5 | This page is under construction. 6 | -------------------------------------------------------------------------------- /sphinx/index.rst: -------------------------------------------------------------------------------- 1 | ISLa: *Inputs on Demand!* 2 | ========================= 3 | 4 | .. note:: 5 | This page is under construction. 6 | 7 | ISLa is a *grammar-aware string constraint solver* with its own specification language. 8 | With ISLa, it is possible to specify *input constraints* like "a variable has to be 9 | defined before it is used," "the 'file name' block must be 100 bytes long," or "the 10 | number of columns in all CSV rows must be identical." 11 | 12 | Building on modern constraint solvers, ISLa provides you with a unique 13 | flexibility to specify—and generate—the system inputs you need. ISLa can be 14 | used for *precise fuzzing:* Keep adding input specifications until you are satisfied 15 | with the number of inputs passing the tested system's parser. Furthermore, you can write 16 | ISLa specifications to carve out specific inputs for testing a *particular program 17 | functionality*. 18 | 19 | Example 20 | ------- 21 | 22 | Our running example is a simple "assignment language" consisting of strings such as 23 | :code:`x := 1 ; y := x`. As a first step towards using ISLa, we formalize this language as 24 | a context-free grammar in `BNF `_: 25 | 26 | .. code-block:: bnf 27 | 28 | ::= 29 | ::= | " ; " 30 | ::= " := " 31 | ::= | 32 | ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | 33 | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | 34 | "u" | "v" | "w" | "x" | "y" | "z" 35 | ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" 36 | 37 | After saving this grammar to a file, say, :code:`assgn.bnf`, we can already generate inputs 38 | from the assignment grammar using the ISLa command line interface: 39 | 40 | .. code-block:: bash 41 | 42 | > isla solve assgn.bnf 43 | s := t 44 | 45 | The following command creates 10 assignments: 46 | 47 | .. code-block:: bash 48 | 49 | > isla solve -n 10 assgn.bnf 50 | a := 6 ; j := x 51 | q := u 52 | e := h ; o := l ; g := w 53 | s := i 54 | k := v ; d := m ; f := 1 55 | n := y ; t := 5 56 | z := 3 ; p := 7 ; b := 0 57 | c := 2 ; r := 4 58 | q := 8 ; l := 9 59 | u := 0 60 | 61 | With ISLa, we can restrict the assignment language on-demand. For example, the ISLa 62 | constraint :code:` = "a"` results in assignment sequences only containing "a" variables: 63 | 64 | .. code-block:: bash 65 | 66 | > isla solve assgn.bnf -n 10 -f 1 --constraint ' = "a"' 67 | a := 5 ; a := a ; a := 7 68 | a := 6 69 | a := a 70 | a := 0 ; a := a ; a := a 71 | a := a ; a := 1 ; a := 4 72 | a := a ; a := 3 ; a := a 73 | a := 8 ; a := 2 74 | a := 9 ; a := a 75 | a := a ; a := 9 76 | a := a ; a := a 77 | 78 | .. note:: 79 | 80 | The setting :code:`-f 1` restricts the number of times that ILSa randomly 81 | instantiates unconstrained input elements to one time. Here, this affects the 82 | :code:`` nonterminals: Without :code:`-f 1`, we would see 10 different variants of the 83 | first input with variying numbers in the first and third assignment. 84 | 85 | Or do we prefer assignments where all digits can be divided by 2 without remainder? No 86 | problem with ISLa: 87 | 88 | .. code-block:: bash 89 | 90 | > isla solve assgn.bnf -n 10 -f 1 -s 2 --constraint "str.to.int() mod 2 = 0" 91 | i := a ; x := 0 ; u := s 92 | p := l ; m := 8 ; b := y 93 | k := c ; t := d ; r := q 94 | j := z 95 | h := 0 96 | e := 4 97 | g := n ; v := f ; w := 4 98 | o := o ; j := a ; c := 0 99 | t := r ; k := 0 ; e := 0 100 | k := t ; f := 8 ; e := 8 101 | 102 | .. note:: 103 | 104 | The :code:`-s` flag specifies how many results for a single query should be obtained 105 | from the SMT solver Z3. We limited this number to 2 (the default is 10—the same 106 | default value is used for the :code:`-f` flag) to obtain a wider diversity of inputs within 107 | the first 10 results. 108 | 109 | The constraints above talk over *all* :code:`` and :code:`` grammar nonterminals in 110 | any derivation tree derived from the assignment language grammar. In addition to such 111 | simple constraints, ISLa allows to explicitly *quantify* over grammar elements using 112 | the :code:`forall` and :code:`exists` keywords. 113 | 114 | Assume that an interpreter for our assignment language rejects inputs where a variable 115 | is accessed that has not been previously assigned a value. This "definition-use" 116 | property, which is a *semantic input property* of the language, is expressed as follows: 117 | 118 | .. code-block:: 119 | 120 | forall assgn_1: 121 | exists assgn_2: ( 122 | before(assgn_2, assgn_1) and 123 | assgn_1.. = assgn_2.) 124 | 125 | Since this is a more lengthy constraint, let us save it in a file :code:`defuse.isla`. The 126 | following command line invocation uses this constraint: 127 | 128 | .. code-block:: bash 129 | 130 | > isla solve -n 10 -f 1 -s 1 assgn.bnf defuse.isla 131 | q := 2 ; m := 1 ; c := 4 132 | p := 8 ; o := 3 ; l := p 133 | z := 7 ; p := 6 ; e := p 134 | d := 5 ; a := d ; h := 9 135 | s := 0 ; x := 0 136 | k := 8 137 | p := 4 ; r := p 138 | p := 6 ; u := p 139 | p := 5 ; v := p 140 | p := 3 ; p := 5 ; w := p 141 | 142 | As we can see, all right-hand side variables occur at the left-hand side of a prior 143 | assignment. 144 | 145 | For more information on the command line interface, run :code:`isla -h`. Each sub command 146 | comes with its own help text; for example, :code:`isla solve -h` provides details on how to 147 | use the :code:`solve` command. 148 | 149 | You can also use the ISLa solver via its Python API: 150 | 151 | .. code-block:: python 152 | 153 | from isla.solver import ISLaSolver 154 | 155 | grammar = ''' 156 | ::= 157 | ::= | " ; " 158 | ::= " := " 159 | ::= | 160 | ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | 161 | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | 162 | "u" | "v" | "w" | "x" | "y" | "z" 163 | ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" 164 | ''' 165 | 166 | constraint = """ 167 | forall assgn_1: 168 | exists assgn_2: ( 169 | before(assgn_2, assgn_1) and 170 | assgn_1.. = assgn_2.) 171 | """ 172 | 173 | solver = ISLaSolver( 174 | grammar=grammar, 175 | formula=constraint, 176 | max_number_free_instantiations=1, # -f 177 | max_number_smt_instantiations=1, # -s 178 | ) 179 | 180 | for _ in range(10): 181 | print(solver.solve()) 182 | 183 | An example output of the above program snippet is: 184 | 185 | .. code-block:: 186 | 187 | q := 7 ; m := 1 ; c := 8 188 | p := 2 ; o := 2 ; l := p 189 | z := 9 ; p := 4 ; e := p 190 | d := 8 ; a := d ; h := 5 191 | s := 0 ; x := 0 192 | k := 7 193 | p := 8 ; r := p 194 | p := 9 ; u := p 195 | p := 4 ; v := p 196 | p := 2 ; p := 1 ; w := p 197 | 198 | Useful Resources 199 | ---------------- 200 | 201 | You want to try ISLa out for your own examples, or need more inspiration? Then, we 202 | recommend our 203 | `interactive ISLa tutorial `_ 204 | providing an easily accessible introduction to the specification and generation of custom system 205 | inputs using ISLa. 206 | 207 | You might also like our :code:`isla create` command: :code:`isla create path` creates a set of 208 | grammar and constraint files along with a README file at the path :code:`path`. All files are 209 | contain explaining comments to help you getting started; we show different constraints 210 | for the assignment language that you can experiment with. 211 | 212 | For diving deeper into ISLa, we recommend the following resources. 213 | 214 | * See :doc:`usage` to get started with ISLa. 215 | 216 | * :doc:`/examples` provides additional examples of ISLa specifications. 217 | 218 | * The :doc:`/developing/index` contains material to get you started with ISLa's code. 219 | 220 | * :doc:`/islaspec` precisely specifies the syntax and semantics of ISLa constraints. 221 | The specification also contains a list of supported default predicates. 222 | 223 | * Our `scientific paper on ISLa `_, 224 | published at ESEC/FSE 2022. The paper describes the ISLa language and solver more 225 | formally. 226 | 227 | * In the directory 228 | `src/isla_formalizations/ `_, 229 | you find our specifications for the subject languages of our experimental evaluation. 230 | 231 | * The files :code:`run_eval_csv.fish`, :code:`run_eval_tar.fish`, and so on, are the scripts we used 232 | to collect and analyze our evaluation data. To analyze ISLa's current performance 233 | yourself, you can run the scripts with the :code:`-h` argument to obtain some guidance on 234 | their parameters (the fish shell is required to use these scripts). 235 | 236 | Indices and tables 237 | ------------------ 238 | 239 | * :ref:`genindex` 240 | * :ref:`modindex` 241 | * :ref:`search` 242 | 243 | .. toctree:: 244 | :maxdepth: 2 245 | :caption: Contents 246 | 247 | usage 248 | examples 249 | islaspec 250 | developing/index 251 | todo 252 | -------------------------------------------------------------------------------- /sphinx/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /sphinx/requirements.txt: -------------------------------------------------------------------------------- 1 | git+https://github.com/rindPHI/isla@main#egg=isla-solver 2 | sphinx-book-theme>=1.0.0 3 | pydata-sphinx-theme==0.13.1 4 | -------------------------------------------------------------------------------- /sphinx/todo.rst: -------------------------------------------------------------------------------- 1 | Open Tasks 2 | ========== 3 | 4 | Ongoing 5 | ------- 6 | 7 | * :doc:`developing/solver` 8 | 9 | Open 10 | ---- 11 | 12 | * :doc:`usage` 13 | * :doc:`examples` 14 | * Documentation of all other modules 15 | 16 | Finished 17 | -------- 18 | 19 | * :doc:`islaspec` 20 | -------------------------------------------------------------------------------- /sphinx/usage.rst: -------------------------------------------------------------------------------- 1 | Installing & Using ISLa 2 | ======================= 3 | 4 | .. note:: 5 | This page is under construction. 6 | 7 | There's currently nothing to see here. For *installation instructions,* see 8 | `ISLa's project page `_. -------------------------------------------------------------------------------- /src/isla/IslaLanguage.g4: -------------------------------------------------------------------------------- 1 | // Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | // Author: Dominic Steinhöfel 3 | // 4 | // This file is part of ISLa. 5 | // 6 | // ISLa is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // ISLa is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with ISLa. If not, see . 18 | 19 | grammar IslaLanguage; 20 | 21 | start: constDecl? formula; 22 | 23 | constDecl: 'const' ID ':' VAR_TYPE ';' ; 24 | 25 | formula: 26 | 'forall' (boundVarType=VAR_TYPE) (varId=ID) ? ('in' (inId=ID | inVarType=VAR_TYPE)) ? ':' formula # Forall 27 | | 'exists' (boundVarType=VAR_TYPE) (varId=ID) ? ('in' (inId=ID | inVarType=VAR_TYPE)) ? ':' formula # Exists 28 | | 'forall' (boundVarType=VAR_TYPE) (varId=ID) ? '=' STRING ('in' (inId=ID | inVarType=VAR_TYPE)) ? ':' formula # ForallMexpr 29 | | 'exists' (boundVarType=VAR_TYPE) (varId=ID) ? '=' STRING ('in' (inId=ID | inVarType=VAR_TYPE)) ? ':' formula # ExistsMexpr 30 | | 'exists' 'int' ID ':' formula # ExistsInt 31 | | 'forall' 'int' ID ':' formula # ForallInt 32 | | 'not' formula # Negation 33 | | formula AND formula # Conjunction 34 | | formula OR formula # Disjunction 35 | | formula XOR formula # ExclusiveOr 36 | | formula IMPLIES_ISLA formula # Implication 37 | | formula 'iff' formula # Equivalence 38 | | ID '(' predicateArg (',' predicateArg) * ')' # PredicateAtom 39 | | '(' formula ')' # ParFormula 40 | | sexpr # SMTFormula 41 | ; 42 | 43 | sexpr: 44 | 'true' # SexprTrue 45 | | 'false' # SexprFalse 46 | | INT # SexprNum 47 | | ID # SexprId 48 | | XPATHEXPR # SexprXPathExpr 49 | | VAR_TYPE # SexprFreeId 50 | | STRING # SexprStr 51 | | (SMT_NONBINARY_OP | smt_binary_op) # SexprOp 52 | | op=SMT_NONBINARY_OP '(' ( sexpr ( ',' sexpr ) * ) ? ')' # SexprPrefix 53 | | sexpr op=SMT_INFIX_RE_STR sexpr # SexprInfixReStr 54 | | sexpr op=(MUL | DIV | MOD) sexpr # SexprInfixMulDiv 55 | | sexpr op=(PLUS | MINUS) sexpr # SexprInfixPlusMinus 56 | | sexpr op=('=' | GEQ | LEQ | GT | LT) sexpr # SexprInfixEq 57 | | '(' op=sexpr sexpr + ')' # SepxrApp 58 | ; 59 | 60 | predicateArg: ID | VAR_TYPE | INT | STRING | XPATHEXPR ; 61 | 62 | AND: 'and' ; 63 | OR: 'or' ; 64 | NOT: 'not' ; 65 | 66 | XOR: 'xor' ; 67 | IMPLIES_SMT: '=>' ; 68 | IMPLIES_ISLA: 'implies' ; 69 | 70 | smt_binary_op: 71 | '=' | GEQ | LEQ | GT | LT | MUL | DIV | MOD | PLUS | MINUS | EXP | SMT_INFIX_RE_STR | AND | OR | IMPLIES_SMT | XOR; 72 | 73 | SMT_INFIX_RE_STR: 74 | 're.++' 75 | | 'str.++' 76 | | 'str.<=' 77 | ; 78 | 79 | SMT_NONBINARY_OP: 80 | ABS 81 | | 're.+' 82 | | 're.*' 83 | | 'str.len' 84 | | 'str.in_re' 85 | | 'str.to_re' 86 | | 're.none' 87 | | 're.all' 88 | | 're.allchar' 89 | | 'str.at' 90 | | 'str.substr' 91 | | 'str.prefixof' 92 | | 'str.suffixof' 93 | | 'str.contains' 94 | | 'str.indexof' 95 | | 'str.replace' 96 | | 'str.replace_all' 97 | | 'str.replace_re' 98 | | 'str.replace_re_all' 99 | | 're.union' 100 | | 're.inter' 101 | | 're.comp' 102 | | 're.diff' 103 | | 're.opt' 104 | | 're.range' 105 | | 're.loop' 106 | | 'str.is_digit' 107 | | 'str.to_code' 108 | | 'str.from_code' 109 | | 'str.to.int' 110 | | 'str.from_int' 111 | ; 112 | 113 | XPATHEXPR: (ID | VAR_TYPE) XPATHSEGMENT + ; 114 | 115 | fragment XPATHSEGMENT: 116 | DOT VAR_TYPE 117 | | DOT VAR_TYPE BROP INT BRCL 118 | | TWODOTS VAR_TYPE 119 | ; 120 | 121 | VAR_TYPE : LT ID GT ; 122 | 123 | DIV: 'div' ; 124 | MOD: 'mod' ; 125 | ABS: 'abs' ; 126 | 127 | STRING: '"' (ESC|.) *? '"'; 128 | ID: INIT_ID_LETTER (ID_LETTER | DIGIT) * ; 129 | INT : '-' ? DIGIT+ ; 130 | ESC : '\\' [btnr"\\] ; 131 | 132 | DOT : '.' ; 133 | TWODOTS : '..' ; 134 | BROP : '[' ; 135 | BRCL : ']' ; 136 | 137 | MUL: '*' ; 138 | PLUS: '+' ; 139 | MINUS: '-' ; 140 | EXP: '^' ; 141 | GEQ: '>=' ; 142 | LEQ: '<=' ; 143 | GT: '>' ; 144 | LT: '<' ; 145 | 146 | WS : [ \t\n\r]+ -> skip ; 147 | LINE_COMMENT : '#' .*? '\n' -> skip ; 148 | 149 | fragment INIT_ID_LETTER : 'a'..'z' | 'A'..'Z' | '_' ; 150 | fragment ID_LETTER : 'a'..'z' | 'A'..'Z' | [_\-.^] ; 151 | fragment DIGIT : '0'..'9' ; 152 | -------------------------------------------------------------------------------- /src/isla/IslaLanguage.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | SMT_INFIX_RE_STR=15 16 | SMT_NONBINARY_OP=16 17 | XPATHEXPR=17 18 | VAR_TYPE=18 19 | AND=19 20 | OR=20 21 | NOT=21 22 | XOR=22 23 | IMPLIES_SMT=23 24 | IMPLIES_ISLA=24 25 | DIV=25 26 | MOD=26 27 | ABS=27 28 | STRING=28 29 | ID=29 30 | INT=30 31 | ESC=31 32 | DOT=32 33 | TWODOTS=33 34 | BROP=34 35 | BRCL=35 36 | MUL=36 37 | PLUS=37 38 | MINUS=38 39 | GEQ=39 40 | LEQ=40 41 | GT=41 42 | LT=42 43 | WS=43 44 | LINE_COMMENT=44 45 | 'const'=1 46 | ':'=2 47 | ';'=3 48 | 'forall'=4 49 | 'in'=5 50 | 'exists'=6 51 | '='=7 52 | 'int'=8 53 | 'iff'=9 54 | '('=10 55 | ','=11 56 | ')'=12 57 | 'true'=13 58 | 'false'=14 59 | 'and'=19 60 | 'or'=20 61 | 'not'=21 62 | 'xor'=22 63 | '=>'=23 64 | 'implies'=24 65 | 'div'=25 66 | 'mod'=26 67 | 'abs'=27 68 | '.'=32 69 | '..'=33 70 | '['=34 71 | ']'=35 72 | '*'=36 73 | '+'=37 74 | '-'=38 75 | '>='=39 76 | '<='=40 77 | '>'=41 78 | '<'=42 79 | -------------------------------------------------------------------------------- /src/isla/MexprLexer.g4: -------------------------------------------------------------------------------- 1 | // Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | // Author: Dominic Steinhöfel 3 | // 4 | // This file is part of ISLa. 5 | // 6 | // ISLa is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // ISLa is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with ISLa. If not, see . 18 | 19 | lexer grammar MexprLexer; 20 | 21 | BRAOP : '{' -> pushMode(VAR_DECL) ; 22 | 23 | OPTOP : '[' -> pushMode(OPTIONAL) ; 24 | 25 | TEXT : (~ [{[]) + ; 26 | 27 | NL : '\n' + -> skip ; 28 | 29 | mode VAR_DECL; 30 | BRACL : '}' -> popMode ; 31 | ID: ID_LETTER (ID_LETTER | DIGIT) * ; 32 | fragment ID_LETTER : 'a'..'z'|'A'..'Z' | [_\-.] ; 33 | fragment DIGIT : '0'..'9' ; 34 | GT: '>' ; 35 | LT: '<' ; 36 | WS : [ \t\n\r]+ -> skip ; 37 | 38 | mode OPTIONAL; 39 | OPTCL : ']' -> popMode ; 40 | OPTTXT : (~ ']') + ; -------------------------------------------------------------------------------- /src/isla/MexprParser.g4: -------------------------------------------------------------------------------- 1 | // Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | // Author: Dominic Steinhöfel 3 | // 4 | // This file is part of ISLa. 5 | // 6 | // ISLa is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // ISLa is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with ISLa. If not, see . 18 | 19 | parser grammar MexprParser; 20 | 21 | options { tokenVocab=MexprLexer; } 22 | 23 | matchExpr: matchExprElement + ; 24 | 25 | matchExprElement: 26 | BRAOP varType ID BRACL # MatchExprVar 27 | | OPTOP OPTTXT OPTCL # MatchExprOptional 28 | | TEXT # MatchExprChars 29 | ; 30 | 31 | varType : LT ID GT ; 32 | -------------------------------------------------------------------------------- /src/isla/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | __version__ = "1.14.4" 20 | -------------------------------------------------------------------------------- /src/isla/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | from isla.cli import main 20 | 21 | main() 22 | -------------------------------------------------------------------------------- /src/isla/bnf.g4: -------------------------------------------------------------------------------- 1 | // Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | // Author: Dominic Steinhöfel 3 | // 4 | // This file is part of ISLa. 5 | // 6 | // ISLa is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // ISLa is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with ISLa. If not, see . 18 | 19 | grammar bnf; 20 | 21 | bnf_grammar: derivation_rule + ; 22 | 23 | derivation_rule: NONTERMINAL '::=' alternative ( '|' alternative ) * ';' ?; 24 | 25 | alternative: ( STRING | NONTERMINAL ) + ; 26 | 27 | NONTERMINAL: '<' ~[<>] + '>' ; 28 | 29 | STRING: '"' (ESC|.) *? '"'; 30 | ESC : '\\' [btnr"\\] ; 31 | 32 | WS : [ \t\n\r]+ -> skip ; 33 | LINE_COMMENT : '#' .*? '\n' -> skip ; 34 | -------------------------------------------------------------------------------- /src/isla/bnf.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | NONTERMINAL=4 5 | STRING=5 6 | ESC=6 7 | WS=7 8 | LINE_COMMENT=8 9 | '::='=1 10 | '|'=2 11 | ';'=3 12 | -------------------------------------------------------------------------------- /src/isla/bnf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rindPHI/isla/1a04b7833d2960cffe037fb88826111769fefbeb/src/isla/bnf/__init__.py -------------------------------------------------------------------------------- /src/isla/bnf/bnf.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '::=' 4 | '|' 5 | ';' 6 | null 7 | null 8 | null 9 | null 10 | null 11 | 12 | token symbolic names: 13 | null 14 | null 15 | null 16 | null 17 | NONTERMINAL 18 | STRING 19 | ESC 20 | WS 21 | LINE_COMMENT 22 | 23 | rule names: 24 | bnf_grammar 25 | derivation_rule 26 | alternative 27 | 28 | 29 | atn: 30 | [4, 1, 8, 30, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 1, 0, 4, 0, 8, 8, 0, 11, 0, 12, 0, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 17, 8, 1, 10, 1, 12, 1, 20, 9, 1, 1, 1, 3, 1, 23, 8, 1, 1, 2, 4, 2, 26, 8, 2, 11, 2, 12, 2, 27, 1, 2, 0, 0, 3, 0, 2, 4, 0, 1, 1, 0, 4, 5, 30, 0, 7, 1, 0, 0, 0, 2, 11, 1, 0, 0, 0, 4, 25, 1, 0, 0, 0, 6, 8, 3, 2, 1, 0, 7, 6, 1, 0, 0, 0, 8, 9, 1, 0, 0, 0, 9, 7, 1, 0, 0, 0, 9, 10, 1, 0, 0, 0, 10, 1, 1, 0, 0, 0, 11, 12, 5, 4, 0, 0, 12, 13, 5, 1, 0, 0, 13, 18, 3, 4, 2, 0, 14, 15, 5, 2, 0, 0, 15, 17, 3, 4, 2, 0, 16, 14, 1, 0, 0, 0, 17, 20, 1, 0, 0, 0, 18, 16, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 22, 1, 0, 0, 0, 20, 18, 1, 0, 0, 0, 21, 23, 5, 3, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 3, 1, 0, 0, 0, 24, 26, 7, 0, 0, 0, 25, 24, 1, 0, 0, 0, 26, 27, 1, 0, 0, 0, 27, 25, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 5, 1, 0, 0, 0, 4, 9, 18, 22, 27] -------------------------------------------------------------------------------- /src/isla/bnf/bnf.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | NONTERMINAL=4 5 | STRING=5 6 | ESC=6 7 | WS=7 8 | LINE_COMMENT=8 9 | '::='=1 10 | '|'=2 11 | ';'=3 12 | -------------------------------------------------------------------------------- /src/isla/bnf/bnfLexer.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '::=' 4 | '|' 5 | ';' 6 | null 7 | null 8 | null 9 | null 10 | null 11 | 12 | token symbolic names: 13 | null 14 | null 15 | null 16 | null 17 | NONTERMINAL 18 | STRING 19 | ESC 20 | WS 21 | LINE_COMMENT 22 | 23 | rule names: 24 | T__0 25 | T__1 26 | T__2 27 | NONTERMINAL 28 | STRING 29 | ESC 30 | WS 31 | LINE_COMMENT 32 | 33 | channel names: 34 | DEFAULT_TOKEN_CHANNEL 35 | HIDDEN 36 | 37 | mode names: 38 | DEFAULT_MODE 39 | 40 | atn: 41 | [4, 0, 8, 64, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 4, 3, 28, 8, 3, 11, 3, 12, 3, 29, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 5, 4, 37, 8, 4, 10, 4, 12, 4, 40, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 4, 6, 48, 8, 6, 11, 6, 12, 6, 49, 1, 6, 1, 6, 1, 7, 1, 7, 5, 7, 56, 8, 7, 10, 7, 12, 7, 59, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 2, 38, 57, 0, 8, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 1, 0, 3, 2, 0, 60, 60, 62, 62, 6, 0, 34, 34, 92, 92, 98, 98, 110, 110, 114, 114, 116, 116, 3, 0, 9, 10, 13, 13, 32, 32, 68, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 1, 17, 1, 0, 0, 0, 3, 21, 1, 0, 0, 0, 5, 23, 1, 0, 0, 0, 7, 25, 1, 0, 0, 0, 9, 33, 1, 0, 0, 0, 11, 43, 1, 0, 0, 0, 13, 47, 1, 0, 0, 0, 15, 53, 1, 0, 0, 0, 17, 18, 5, 58, 0, 0, 18, 19, 5, 58, 0, 0, 19, 20, 5, 61, 0, 0, 20, 2, 1, 0, 0, 0, 21, 22, 5, 124, 0, 0, 22, 4, 1, 0, 0, 0, 23, 24, 5, 59, 0, 0, 24, 6, 1, 0, 0, 0, 25, 27, 5, 60, 0, 0, 26, 28, 8, 0, 0, 0, 27, 26, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 27, 1, 0, 0, 0, 29, 30, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 32, 5, 62, 0, 0, 32, 8, 1, 0, 0, 0, 33, 38, 5, 34, 0, 0, 34, 37, 3, 11, 5, 0, 35, 37, 9, 0, 0, 0, 36, 34, 1, 0, 0, 0, 36, 35, 1, 0, 0, 0, 37, 40, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 38, 36, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 41, 42, 5, 34, 0, 0, 42, 10, 1, 0, 0, 0, 43, 44, 5, 92, 0, 0, 44, 45, 7, 1, 0, 0, 45, 12, 1, 0, 0, 0, 46, 48, 7, 2, 0, 0, 47, 46, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 47, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 6, 6, 0, 0, 52, 14, 1, 0, 0, 0, 53, 57, 5, 35, 0, 0, 54, 56, 9, 0, 0, 0, 55, 54, 1, 0, 0, 0, 56, 59, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 57, 55, 1, 0, 0, 0, 58, 60, 1, 0, 0, 0, 59, 57, 1, 0, 0, 0, 60, 61, 5, 10, 0, 0, 61, 62, 1, 0, 0, 0, 62, 63, 6, 7, 0, 0, 63, 16, 1, 0, 0, 0, 6, 0, 29, 36, 38, 49, 57, 1, 6, 0, 0] -------------------------------------------------------------------------------- /src/isla/bnf/bnfLexer.py: -------------------------------------------------------------------------------- 1 | # Generated from bnf.g4 by ANTLR 4.13.0 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 | def serializedATN(): 12 | return [ 13 | 4,0,8,64,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2, 14 | 6,7,6,2,7,7,7,1,0,1,0,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,4,3,28,8,3, 15 | 11,3,12,3,29,1,3,1,3,1,4,1,4,1,4,5,4,37,8,4,10,4,12,4,40,9,4,1,4, 16 | 1,4,1,5,1,5,1,5,1,6,4,6,48,8,6,11,6,12,6,49,1,6,1,6,1,7,1,7,5,7, 17 | 56,8,7,10,7,12,7,59,9,7,1,7,1,7,1,7,1,7,2,38,57,0,8,1,1,3,2,5,3, 18 | 7,4,9,5,11,6,13,7,15,8,1,0,3,2,0,60,60,62,62,6,0,34,34,92,92,98, 19 | 98,110,110,114,114,116,116,3,0,9,10,13,13,32,32,68,0,1,1,0,0,0,0, 20 | 3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13, 21 | 1,0,0,0,0,15,1,0,0,0,1,17,1,0,0,0,3,21,1,0,0,0,5,23,1,0,0,0,7,25, 22 | 1,0,0,0,9,33,1,0,0,0,11,43,1,0,0,0,13,47,1,0,0,0,15,53,1,0,0,0,17, 23 | 18,5,58,0,0,18,19,5,58,0,0,19,20,5,61,0,0,20,2,1,0,0,0,21,22,5,124, 24 | 0,0,22,4,1,0,0,0,23,24,5,59,0,0,24,6,1,0,0,0,25,27,5,60,0,0,26,28, 25 | 8,0,0,0,27,26,1,0,0,0,28,29,1,0,0,0,29,27,1,0,0,0,29,30,1,0,0,0, 26 | 30,31,1,0,0,0,31,32,5,62,0,0,32,8,1,0,0,0,33,38,5,34,0,0,34,37,3, 27 | 11,5,0,35,37,9,0,0,0,36,34,1,0,0,0,36,35,1,0,0,0,37,40,1,0,0,0,38, 28 | 39,1,0,0,0,38,36,1,0,0,0,39,41,1,0,0,0,40,38,1,0,0,0,41,42,5,34, 29 | 0,0,42,10,1,0,0,0,43,44,5,92,0,0,44,45,7,1,0,0,45,12,1,0,0,0,46, 30 | 48,7,2,0,0,47,46,1,0,0,0,48,49,1,0,0,0,49,47,1,0,0,0,49,50,1,0,0, 31 | 0,50,51,1,0,0,0,51,52,6,6,0,0,52,14,1,0,0,0,53,57,5,35,0,0,54,56, 32 | 9,0,0,0,55,54,1,0,0,0,56,59,1,0,0,0,57,58,1,0,0,0,57,55,1,0,0,0, 33 | 58,60,1,0,0,0,59,57,1,0,0,0,60,61,5,10,0,0,61,62,1,0,0,0,62,63,6, 34 | 7,0,0,63,16,1,0,0,0,6,0,29,36,38,49,57,1,6,0,0 35 | ] 36 | 37 | class bnfLexer(Lexer): 38 | 39 | atn = ATNDeserializer().deserialize(serializedATN()) 40 | 41 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 42 | 43 | T__0 = 1 44 | T__1 = 2 45 | T__2 = 3 46 | NONTERMINAL = 4 47 | STRING = 5 48 | ESC = 6 49 | WS = 7 50 | LINE_COMMENT = 8 51 | 52 | channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] 53 | 54 | modeNames = [ "DEFAULT_MODE" ] 55 | 56 | literalNames = [ "", 57 | "'::='", "'|'", "';'" ] 58 | 59 | symbolicNames = [ "", 60 | "NONTERMINAL", "STRING", "ESC", "WS", "LINE_COMMENT" ] 61 | 62 | ruleNames = [ "T__0", "T__1", "T__2", "NONTERMINAL", "STRING", "ESC", 63 | "WS", "LINE_COMMENT" ] 64 | 65 | grammarFileName = "bnf.g4" 66 | 67 | def __init__(self, input=None, output:TextIO = sys.stdout): 68 | super().__init__(input, output) 69 | self.checkVersion("4.13.0") 70 | self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) 71 | self._actions = None 72 | self._predicates = None 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/isla/bnf/bnfLexer.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | NONTERMINAL=4 5 | STRING=5 6 | ESC=6 7 | WS=7 8 | LINE_COMMENT=8 9 | '::='=1 10 | '|'=2 11 | ';'=3 12 | -------------------------------------------------------------------------------- /src/isla/bnf/bnfListener.py: -------------------------------------------------------------------------------- 1 | # Generated from bnf.g4 by ANTLR 4.13.0 2 | from antlr4 import * 3 | if "." in __name__: 4 | from .bnfParser import bnfParser 5 | else: 6 | from bnfParser import bnfParser 7 | 8 | # This class defines a complete listener for a parse tree produced by bnfParser. 9 | class bnfListener(ParseTreeListener): 10 | 11 | # Enter a parse tree produced by bnfParser#bnf_grammar. 12 | def enterBnf_grammar(self, ctx:bnfParser.Bnf_grammarContext): 13 | pass 14 | 15 | # Exit a parse tree produced by bnfParser#bnf_grammar. 16 | def exitBnf_grammar(self, ctx:bnfParser.Bnf_grammarContext): 17 | pass 18 | 19 | 20 | # Enter a parse tree produced by bnfParser#derivation_rule. 21 | def enterDerivation_rule(self, ctx:bnfParser.Derivation_ruleContext): 22 | pass 23 | 24 | # Exit a parse tree produced by bnfParser#derivation_rule. 25 | def exitDerivation_rule(self, ctx:bnfParser.Derivation_ruleContext): 26 | pass 27 | 28 | 29 | # Enter a parse tree produced by bnfParser#alternative. 30 | def enterAlternative(self, ctx:bnfParser.AlternativeContext): 31 | pass 32 | 33 | # Exit a parse tree produced by bnfParser#alternative. 34 | def exitAlternative(self, ctx:bnfParser.AlternativeContext): 35 | pass 36 | 37 | 38 | 39 | del bnfParser -------------------------------------------------------------------------------- /src/isla/bnf/bnfParser.py: -------------------------------------------------------------------------------- 1 | # Generated from bnf.g4 by ANTLR 4.13.0 2 | # encoding: utf-8 3 | from antlr4 import * 4 | from io import StringIO 5 | import sys 6 | if sys.version_info[1] > 5: 7 | from typing import TextIO 8 | else: 9 | from typing.io import TextIO 10 | 11 | def serializedATN(): 12 | return [ 13 | 4,1,8,30,2,0,7,0,2,1,7,1,2,2,7,2,1,0,4,0,8,8,0,11,0,12,0,9,1,1,1, 14 | 1,1,1,1,1,1,1,5,1,17,8,1,10,1,12,1,20,9,1,1,1,3,1,23,8,1,1,2,4,2, 15 | 26,8,2,11,2,12,2,27,1,2,0,0,3,0,2,4,0,1,1,0,4,5,30,0,7,1,0,0,0,2, 16 | 11,1,0,0,0,4,25,1,0,0,0,6,8,3,2,1,0,7,6,1,0,0,0,8,9,1,0,0,0,9,7, 17 | 1,0,0,0,9,10,1,0,0,0,10,1,1,0,0,0,11,12,5,4,0,0,12,13,5,1,0,0,13, 18 | 18,3,4,2,0,14,15,5,2,0,0,15,17,3,4,2,0,16,14,1,0,0,0,17,20,1,0,0, 19 | 0,18,16,1,0,0,0,18,19,1,0,0,0,19,22,1,0,0,0,20,18,1,0,0,0,21,23, 20 | 5,3,0,0,22,21,1,0,0,0,22,23,1,0,0,0,23,3,1,0,0,0,24,26,7,0,0,0,25, 21 | 24,1,0,0,0,26,27,1,0,0,0,27,25,1,0,0,0,27,28,1,0,0,0,28,5,1,0,0, 22 | 0,4,9,18,22,27 23 | ] 24 | 25 | class bnfParser ( Parser ): 26 | 27 | grammarFileName = "bnf.g4" 28 | 29 | atn = ATNDeserializer().deserialize(serializedATN()) 30 | 31 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 32 | 33 | sharedContextCache = PredictionContextCache() 34 | 35 | literalNames = [ "", "'::='", "'|'", "';'" ] 36 | 37 | symbolicNames = [ "", "", "", "", 38 | "NONTERMINAL", "STRING", "ESC", "WS", "LINE_COMMENT" ] 39 | 40 | RULE_bnf_grammar = 0 41 | RULE_derivation_rule = 1 42 | RULE_alternative = 2 43 | 44 | ruleNames = [ "bnf_grammar", "derivation_rule", "alternative" ] 45 | 46 | EOF = Token.EOF 47 | T__0=1 48 | T__1=2 49 | T__2=3 50 | NONTERMINAL=4 51 | STRING=5 52 | ESC=6 53 | WS=7 54 | LINE_COMMENT=8 55 | 56 | def __init__(self, input:TokenStream, output:TextIO = sys.stdout): 57 | super().__init__(input, output) 58 | self.checkVersion("4.13.0") 59 | self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) 60 | self._predicates = None 61 | 62 | 63 | 64 | 65 | class Bnf_grammarContext(ParserRuleContext): 66 | __slots__ = 'parser' 67 | 68 | def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): 69 | super().__init__(parent, invokingState) 70 | self.parser = parser 71 | 72 | def derivation_rule(self, i:int=None): 73 | if i is None: 74 | return self.getTypedRuleContexts(bnfParser.Derivation_ruleContext) 75 | else: 76 | return self.getTypedRuleContext(bnfParser.Derivation_ruleContext,i) 77 | 78 | 79 | def getRuleIndex(self): 80 | return bnfParser.RULE_bnf_grammar 81 | 82 | def enterRule(self, listener:ParseTreeListener): 83 | if hasattr( listener, "enterBnf_grammar" ): 84 | listener.enterBnf_grammar(self) 85 | 86 | def exitRule(self, listener:ParseTreeListener): 87 | if hasattr( listener, "exitBnf_grammar" ): 88 | listener.exitBnf_grammar(self) 89 | 90 | 91 | 92 | 93 | def bnf_grammar(self): 94 | 95 | localctx = bnfParser.Bnf_grammarContext(self, self._ctx, self.state) 96 | self.enterRule(localctx, 0, self.RULE_bnf_grammar) 97 | self._la = 0 # Token type 98 | try: 99 | self.enterOuterAlt(localctx, 1) 100 | self.state = 7 101 | self._errHandler.sync(self) 102 | _la = self._input.LA(1) 103 | while True: 104 | self.state = 6 105 | self.derivation_rule() 106 | self.state = 9 107 | self._errHandler.sync(self) 108 | _la = self._input.LA(1) 109 | if not (_la==4): 110 | break 111 | 112 | except RecognitionException as re: 113 | localctx.exception = re 114 | self._errHandler.reportError(self, re) 115 | self._errHandler.recover(self, re) 116 | finally: 117 | self.exitRule() 118 | return localctx 119 | 120 | 121 | class Derivation_ruleContext(ParserRuleContext): 122 | __slots__ = 'parser' 123 | 124 | def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): 125 | super().__init__(parent, invokingState) 126 | self.parser = parser 127 | 128 | def NONTERMINAL(self): 129 | return self.getToken(bnfParser.NONTERMINAL, 0) 130 | 131 | def alternative(self, i:int=None): 132 | if i is None: 133 | return self.getTypedRuleContexts(bnfParser.AlternativeContext) 134 | else: 135 | return self.getTypedRuleContext(bnfParser.AlternativeContext,i) 136 | 137 | 138 | def getRuleIndex(self): 139 | return bnfParser.RULE_derivation_rule 140 | 141 | def enterRule(self, listener:ParseTreeListener): 142 | if hasattr( listener, "enterDerivation_rule" ): 143 | listener.enterDerivation_rule(self) 144 | 145 | def exitRule(self, listener:ParseTreeListener): 146 | if hasattr( listener, "exitDerivation_rule" ): 147 | listener.exitDerivation_rule(self) 148 | 149 | 150 | 151 | 152 | def derivation_rule(self): 153 | 154 | localctx = bnfParser.Derivation_ruleContext(self, self._ctx, self.state) 155 | self.enterRule(localctx, 2, self.RULE_derivation_rule) 156 | self._la = 0 # Token type 157 | try: 158 | self.enterOuterAlt(localctx, 1) 159 | self.state = 11 160 | self.match(bnfParser.NONTERMINAL) 161 | self.state = 12 162 | self.match(bnfParser.T__0) 163 | self.state = 13 164 | self.alternative() 165 | self.state = 18 166 | self._errHandler.sync(self) 167 | _la = self._input.LA(1) 168 | while _la==2: 169 | self.state = 14 170 | self.match(bnfParser.T__1) 171 | self.state = 15 172 | self.alternative() 173 | self.state = 20 174 | self._errHandler.sync(self) 175 | _la = self._input.LA(1) 176 | 177 | self.state = 22 178 | self._errHandler.sync(self) 179 | _la = self._input.LA(1) 180 | if _la==3: 181 | self.state = 21 182 | self.match(bnfParser.T__2) 183 | 184 | 185 | except RecognitionException as re: 186 | localctx.exception = re 187 | self._errHandler.reportError(self, re) 188 | self._errHandler.recover(self, re) 189 | finally: 190 | self.exitRule() 191 | return localctx 192 | 193 | 194 | class AlternativeContext(ParserRuleContext): 195 | __slots__ = 'parser' 196 | 197 | def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): 198 | super().__init__(parent, invokingState) 199 | self.parser = parser 200 | 201 | def STRING(self, i:int=None): 202 | if i is None: 203 | return self.getTokens(bnfParser.STRING) 204 | else: 205 | return self.getToken(bnfParser.STRING, i) 206 | 207 | def NONTERMINAL(self, i:int=None): 208 | if i is None: 209 | return self.getTokens(bnfParser.NONTERMINAL) 210 | else: 211 | return self.getToken(bnfParser.NONTERMINAL, i) 212 | 213 | def getRuleIndex(self): 214 | return bnfParser.RULE_alternative 215 | 216 | def enterRule(self, listener:ParseTreeListener): 217 | if hasattr( listener, "enterAlternative" ): 218 | listener.enterAlternative(self) 219 | 220 | def exitRule(self, listener:ParseTreeListener): 221 | if hasattr( listener, "exitAlternative" ): 222 | listener.exitAlternative(self) 223 | 224 | 225 | 226 | 227 | def alternative(self): 228 | 229 | localctx = bnfParser.AlternativeContext(self, self._ctx, self.state) 230 | self.enterRule(localctx, 4, self.RULE_alternative) 231 | self._la = 0 # Token type 232 | try: 233 | self.enterOuterAlt(localctx, 1) 234 | self.state = 25 235 | self._errHandler.sync(self) 236 | _alt = 1 237 | while _alt!=2 and _alt!=ATN.INVALID_ALT_NUMBER: 238 | if _alt == 1: 239 | self.state = 24 240 | _la = self._input.LA(1) 241 | if not(_la==4 or _la==5): 242 | self._errHandler.recoverInline(self) 243 | else: 244 | self._errHandler.reportMatch(self) 245 | self.consume() 246 | 247 | else: 248 | raise NoViableAltException(self) 249 | self.state = 27 250 | self._errHandler.sync(self) 251 | _alt = self._interp.adaptivePredict(self._input,3,self._ctx) 252 | 253 | except RecognitionException as re: 254 | localctx.exception = re 255 | self._errHandler.reportError(self, re) 256 | self._errHandler.recover(self, re) 257 | finally: 258 | self.exitRule() 259 | return localctx 260 | 261 | 262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /src/isla/global_config.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2023 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | from dataclasses import dataclass 19 | 20 | 21 | @dataclass 22 | class GlobalConfig: 23 | # If set to False, all assertions guarded by `isla.helpers.assertions_activated` 24 | # will not be executed. 25 | assertions_activated: bool = True 26 | 27 | 28 | GLOBAL_CONFIG = GlobalConfig() 29 | -------------------------------------------------------------------------------- /src/isla/isla_language/IslaLanguage.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | 'const' 4 | ':' 5 | ';' 6 | 'forall' 7 | 'in' 8 | 'exists' 9 | '=' 10 | 'int' 11 | 'iff' 12 | '(' 13 | ',' 14 | ')' 15 | 'true' 16 | 'false' 17 | 'and' 18 | 'or' 19 | 'not' 20 | 'xor' 21 | '=>' 22 | 'implies' 23 | null 24 | null 25 | null 26 | null 27 | 'div' 28 | 'mod' 29 | 'abs' 30 | null 31 | null 32 | null 33 | null 34 | '.' 35 | '..' 36 | '[' 37 | ']' 38 | '*' 39 | '+' 40 | '-' 41 | '^' 42 | '>=' 43 | '<=' 44 | '>' 45 | '<' 46 | null 47 | null 48 | 49 | token symbolic names: 50 | null 51 | null 52 | null 53 | null 54 | null 55 | null 56 | null 57 | null 58 | null 59 | null 60 | null 61 | null 62 | null 63 | null 64 | null 65 | AND 66 | OR 67 | NOT 68 | XOR 69 | IMPLIES_SMT 70 | IMPLIES_ISLA 71 | SMT_INFIX_RE_STR 72 | SMT_NONBINARY_OP 73 | XPATHEXPR 74 | VAR_TYPE 75 | DIV 76 | MOD 77 | ABS 78 | STRING 79 | ID 80 | INT 81 | ESC 82 | DOT 83 | TWODOTS 84 | BROP 85 | BRCL 86 | MUL 87 | PLUS 88 | MINUS 89 | EXP 90 | GEQ 91 | LEQ 92 | GT 93 | LT 94 | WS 95 | LINE_COMMENT 96 | 97 | rule names: 98 | start 99 | constDecl 100 | formula 101 | sexpr 102 | predicateArg 103 | smt_binary_op 104 | 105 | 106 | atn: 107 | [4, 1, 45, 193, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 3, 0, 14, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 28, 8, 2, 1, 2, 1, 2, 1, 2, 3, 2, 33, 8, 2, 3, 2, 35, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 42, 8, 2, 1, 2, 1, 2, 1, 2, 3, 2, 47, 8, 2, 3, 2, 49, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 56, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 63, 8, 2, 3, 2, 65, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 72, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 79, 8, 2, 3, 2, 81, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 102, 8, 2, 10, 2, 12, 2, 105, 9, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 114, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 5, 2, 131, 8, 2, 10, 2, 12, 2, 134, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 146, 8, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 153, 8, 3, 10, 3, 12, 3, 156, 9, 3, 3, 3, 158, 8, 3, 1, 3, 1, 3, 1, 3, 1, 3, 4, 3, 164, 8, 3, 11, 3, 12, 3, 165, 1, 3, 1, 3, 3, 3, 170, 8, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 184, 8, 3, 10, 3, 12, 3, 187, 9, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 0, 2, 4, 6, 6, 0, 2, 4, 6, 8, 10, 0, 5, 2, 0, 25, 26, 36, 36, 1, 0, 37, 38, 2, 0, 7, 7, 40, 43, 2, 0, 23, 24, 28, 30, 6, 0, 7, 7, 15, 16, 18, 19, 21, 21, 25, 26, 36, 43, 231, 0, 13, 1, 0, 0, 0, 2, 17, 1, 0, 0, 0, 4, 113, 1, 0, 0, 0, 6, 169, 1, 0, 0, 0, 8, 188, 1, 0, 0, 0, 10, 190, 1, 0, 0, 0, 12, 14, 3, 2, 1, 0, 13, 12, 1, 0, 0, 0, 13, 14, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 16, 3, 4, 2, 0, 16, 1, 1, 0, 0, 0, 17, 18, 5, 1, 0, 0, 18, 19, 5, 29, 0, 0, 19, 20, 5, 2, 0, 0, 20, 21, 5, 24, 0, 0, 21, 22, 5, 3, 0, 0, 22, 3, 1, 0, 0, 0, 23, 24, 6, 2, -1, 0, 24, 25, 5, 4, 0, 0, 25, 27, 5, 24, 0, 0, 26, 28, 5, 29, 0, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 34, 1, 0, 0, 0, 29, 32, 5, 5, 0, 0, 30, 33, 5, 29, 0, 0, 31, 33, 5, 24, 0, 0, 32, 30, 1, 0, 0, 0, 32, 31, 1, 0, 0, 0, 33, 35, 1, 0, 0, 0, 34, 29, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 114, 3, 4, 2, 15, 38, 39, 5, 6, 0, 0, 39, 41, 5, 24, 0, 0, 40, 42, 5, 29, 0, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 48, 1, 0, 0, 0, 43, 46, 5, 5, 0, 0, 44, 47, 5, 29, 0, 0, 45, 47, 5, 24, 0, 0, 46, 44, 1, 0, 0, 0, 46, 45, 1, 0, 0, 0, 47, 49, 1, 0, 0, 0, 48, 43, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 51, 5, 2, 0, 0, 51, 114, 3, 4, 2, 14, 52, 53, 5, 4, 0, 0, 53, 55, 5, 24, 0, 0, 54, 56, 5, 29, 0, 0, 55, 54, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 58, 5, 7, 0, 0, 58, 64, 5, 28, 0, 0, 59, 62, 5, 5, 0, 0, 60, 63, 5, 29, 0, 0, 61, 63, 5, 24, 0, 0, 62, 60, 1, 0, 0, 0, 62, 61, 1, 0, 0, 0, 63, 65, 1, 0, 0, 0, 64, 59, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 66, 1, 0, 0, 0, 66, 67, 5, 2, 0, 0, 67, 114, 3, 4, 2, 13, 68, 69, 5, 6, 0, 0, 69, 71, 5, 24, 0, 0, 70, 72, 5, 29, 0, 0, 71, 70, 1, 0, 0, 0, 71, 72, 1, 0, 0, 0, 72, 73, 1, 0, 0, 0, 73, 74, 5, 7, 0, 0, 74, 80, 5, 28, 0, 0, 75, 78, 5, 5, 0, 0, 76, 79, 5, 29, 0, 0, 77, 79, 5, 24, 0, 0, 78, 76, 1, 0, 0, 0, 78, 77, 1, 0, 0, 0, 79, 81, 1, 0, 0, 0, 80, 75, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 83, 5, 2, 0, 0, 83, 114, 3, 4, 2, 12, 84, 85, 5, 6, 0, 0, 85, 86, 5, 8, 0, 0, 86, 87, 5, 29, 0, 0, 87, 88, 5, 2, 0, 0, 88, 114, 3, 4, 2, 11, 89, 90, 5, 4, 0, 0, 90, 91, 5, 8, 0, 0, 91, 92, 5, 29, 0, 0, 92, 93, 5, 2, 0, 0, 93, 114, 3, 4, 2, 10, 94, 95, 5, 17, 0, 0, 95, 114, 3, 4, 2, 9, 96, 97, 5, 29, 0, 0, 97, 98, 5, 10, 0, 0, 98, 103, 3, 8, 4, 0, 99, 100, 5, 11, 0, 0, 100, 102, 3, 8, 4, 0, 101, 99, 1, 0, 0, 0, 102, 105, 1, 0, 0, 0, 103, 101, 1, 0, 0, 0, 103, 104, 1, 0, 0, 0, 104, 106, 1, 0, 0, 0, 105, 103, 1, 0, 0, 0, 106, 107, 5, 12, 0, 0, 107, 114, 1, 0, 0, 0, 108, 109, 5, 10, 0, 0, 109, 110, 3, 4, 2, 0, 110, 111, 5, 12, 0, 0, 111, 114, 1, 0, 0, 0, 112, 114, 3, 6, 3, 0, 113, 23, 1, 0, 0, 0, 113, 38, 1, 0, 0, 0, 113, 52, 1, 0, 0, 0, 113, 68, 1, 0, 0, 0, 113, 84, 1, 0, 0, 0, 113, 89, 1, 0, 0, 0, 113, 94, 1, 0, 0, 0, 113, 96, 1, 0, 0, 0, 113, 108, 1, 0, 0, 0, 113, 112, 1, 0, 0, 0, 114, 132, 1, 0, 0, 0, 115, 116, 10, 8, 0, 0, 116, 117, 5, 15, 0, 0, 117, 131, 3, 4, 2, 9, 118, 119, 10, 7, 0, 0, 119, 120, 5, 16, 0, 0, 120, 131, 3, 4, 2, 8, 121, 122, 10, 6, 0, 0, 122, 123, 5, 18, 0, 0, 123, 131, 3, 4, 2, 7, 124, 125, 10, 5, 0, 0, 125, 126, 5, 20, 0, 0, 126, 131, 3, 4, 2, 6, 127, 128, 10, 4, 0, 0, 128, 129, 5, 9, 0, 0, 129, 131, 3, 4, 2, 5, 130, 115, 1, 0, 0, 0, 130, 118, 1, 0, 0, 0, 130, 121, 1, 0, 0, 0, 130, 124, 1, 0, 0, 0, 130, 127, 1, 0, 0, 0, 131, 134, 1, 0, 0, 0, 132, 130, 1, 0, 0, 0, 132, 133, 1, 0, 0, 0, 133, 5, 1, 0, 0, 0, 134, 132, 1, 0, 0, 0, 135, 136, 6, 3, -1, 0, 136, 170, 5, 13, 0, 0, 137, 170, 5, 14, 0, 0, 138, 170, 5, 30, 0, 0, 139, 170, 5, 29, 0, 0, 140, 170, 5, 23, 0, 0, 141, 170, 5, 24, 0, 0, 142, 170, 5, 28, 0, 0, 143, 146, 5, 22, 0, 0, 144, 146, 3, 10, 5, 0, 145, 143, 1, 0, 0, 0, 145, 144, 1, 0, 0, 0, 146, 170, 1, 0, 0, 0, 147, 148, 5, 22, 0, 0, 148, 157, 5, 10, 0, 0, 149, 154, 3, 6, 3, 0, 150, 151, 5, 11, 0, 0, 151, 153, 3, 6, 3, 0, 152, 150, 1, 0, 0, 0, 153, 156, 1, 0, 0, 0, 154, 152, 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 158, 1, 0, 0, 0, 156, 154, 1, 0, 0, 0, 157, 149, 1, 0, 0, 0, 157, 158, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 170, 5, 12, 0, 0, 160, 161, 5, 10, 0, 0, 161, 163, 3, 6, 3, 0, 162, 164, 3, 6, 3, 0, 163, 162, 1, 0, 0, 0, 164, 165, 1, 0, 0, 0, 165, 163, 1, 0, 0, 0, 165, 166, 1, 0, 0, 0, 166, 167, 1, 0, 0, 0, 167, 168, 5, 12, 0, 0, 168, 170, 1, 0, 0, 0, 169, 135, 1, 0, 0, 0, 169, 137, 1, 0, 0, 0, 169, 138, 1, 0, 0, 0, 169, 139, 1, 0, 0, 0, 169, 140, 1, 0, 0, 0, 169, 141, 1, 0, 0, 0, 169, 142, 1, 0, 0, 0, 169, 145, 1, 0, 0, 0, 169, 147, 1, 0, 0, 0, 169, 160, 1, 0, 0, 0, 170, 185, 1, 0, 0, 0, 171, 172, 10, 5, 0, 0, 172, 173, 5, 21, 0, 0, 173, 184, 3, 6, 3, 6, 174, 175, 10, 4, 0, 0, 175, 176, 7, 0, 0, 0, 176, 184, 3, 6, 3, 5, 177, 178, 10, 3, 0, 0, 178, 179, 7, 1, 0, 0, 179, 184, 3, 6, 3, 4, 180, 181, 10, 2, 0, 0, 181, 182, 7, 2, 0, 0, 182, 184, 3, 6, 3, 3, 183, 171, 1, 0, 0, 0, 183, 174, 1, 0, 0, 0, 183, 177, 1, 0, 0, 0, 183, 180, 1, 0, 0, 0, 184, 187, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 185, 186, 1, 0, 0, 0, 186, 7, 1, 0, 0, 0, 187, 185, 1, 0, 0, 0, 188, 189, 7, 3, 0, 0, 189, 9, 1, 0, 0, 0, 190, 191, 7, 4, 0, 0, 191, 11, 1, 0, 0, 0, 24, 13, 27, 32, 34, 41, 46, 48, 55, 62, 64, 71, 78, 80, 103, 113, 130, 132, 145, 154, 157, 165, 169, 183, 185] -------------------------------------------------------------------------------- /src/isla/isla_language/IslaLanguage.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | AND=15 16 | OR=16 17 | NOT=17 18 | XOR=18 19 | IMPLIES_SMT=19 20 | IMPLIES_ISLA=20 21 | SMT_INFIX_RE_STR=21 22 | SMT_NONBINARY_OP=22 23 | XPATHEXPR=23 24 | VAR_TYPE=24 25 | DIV=25 26 | MOD=26 27 | ABS=27 28 | STRING=28 29 | ID=29 30 | INT=30 31 | ESC=31 32 | DOT=32 33 | TWODOTS=33 34 | BROP=34 35 | BRCL=35 36 | MUL=36 37 | PLUS=37 38 | MINUS=38 39 | EXP=39 40 | GEQ=40 41 | LEQ=41 42 | GT=42 43 | LT=43 44 | WS=44 45 | LINE_COMMENT=45 46 | 'const'=1 47 | ':'=2 48 | ';'=3 49 | 'forall'=4 50 | 'in'=5 51 | 'exists'=6 52 | '='=7 53 | 'int'=8 54 | 'iff'=9 55 | '('=10 56 | ','=11 57 | ')'=12 58 | 'true'=13 59 | 'false'=14 60 | 'and'=15 61 | 'or'=16 62 | 'not'=17 63 | 'xor'=18 64 | '=>'=19 65 | 'implies'=20 66 | 'div'=25 67 | 'mod'=26 68 | 'abs'=27 69 | '.'=32 70 | '..'=33 71 | '['=34 72 | ']'=35 73 | '*'=36 74 | '+'=37 75 | '-'=38 76 | '^'=39 77 | '>='=40 78 | '<='=41 79 | '>'=42 80 | '<'=43 81 | -------------------------------------------------------------------------------- /src/isla/isla_language/IslaLanguageLexer.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | AND=15 16 | OR=16 17 | NOT=17 18 | XOR=18 19 | IMPLIES_SMT=19 20 | IMPLIES_ISLA=20 21 | SMT_INFIX_RE_STR=21 22 | SMT_NONBINARY_OP=22 23 | XPATHEXPR=23 24 | VAR_TYPE=24 25 | DIV=25 26 | MOD=26 27 | ABS=27 28 | STRING=28 29 | ID=29 30 | INT=30 31 | ESC=31 32 | DOT=32 33 | TWODOTS=33 34 | BROP=34 35 | BRCL=35 36 | MUL=36 37 | PLUS=37 38 | MINUS=38 39 | EXP=39 40 | GEQ=40 41 | LEQ=41 42 | GT=42 43 | LT=43 44 | WS=44 45 | LINE_COMMENT=45 46 | 'const'=1 47 | ':'=2 48 | ';'=3 49 | 'forall'=4 50 | 'in'=5 51 | 'exists'=6 52 | '='=7 53 | 'int'=8 54 | 'iff'=9 55 | '('=10 56 | ','=11 57 | ')'=12 58 | 'true'=13 59 | 'false'=14 60 | 'and'=15 61 | 'or'=16 62 | 'not'=17 63 | 'xor'=18 64 | '=>'=19 65 | 'implies'=20 66 | 'div'=25 67 | 'mod'=26 68 | 'abs'=27 69 | '.'=32 70 | '..'=33 71 | '['=34 72 | ']'=35 73 | '*'=36 74 | '+'=37 75 | '-'=38 76 | '^'=39 77 | '>='=40 78 | '<='=41 79 | '>'=42 80 | '<'=43 81 | -------------------------------------------------------------------------------- /src/isla/isla_language/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rindPHI/isla/1a04b7833d2960cffe037fb88826111769fefbeb/src/isla/isla_language/__init__.py -------------------------------------------------------------------------------- /src/isla/isla_shortcuts.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | import functools 19 | from functools import cache 20 | from typing import Union 21 | 22 | import z3 23 | 24 | from isla.derivation_tree import DerivationTree 25 | from isla.isla_predicates import ( 26 | BEFORE_PREDICATE, 27 | DIFFERENT_POSITION_PREDICATE, 28 | ) 29 | from isla.language import ( 30 | BoundVariable, 31 | Formula, 32 | BindExpression, 33 | Variable, 34 | ForallFormula, 35 | ExistsFormula, 36 | StructuralPredicateFormula, 37 | SMTFormula, 38 | ) 39 | 40 | 41 | def bexpr(terminal_symbol: str) -> BindExpression: 42 | return BindExpression(terminal_symbol) 43 | 44 | 45 | def forall_bind( 46 | bind_expression: Union[BindExpression, BoundVariable], 47 | bound_variable: Union[BoundVariable, str], 48 | in_variable: Union[Variable, DerivationTree], 49 | inner_formula: Formula, 50 | ) -> ForallFormula: 51 | return ForallFormula(bound_variable, in_variable, inner_formula, bind_expression) 52 | 53 | 54 | def exists_bind( 55 | bind_expression: Union[BindExpression, BoundVariable], 56 | bound_variable: Union[BoundVariable, str], 57 | in_variable: Union[Variable, DerivationTree], 58 | inner_formula: Formula, 59 | ) -> ExistsFormula: 60 | return ExistsFormula(bound_variable, in_variable, inner_formula, bind_expression) 61 | 62 | 63 | def forall( 64 | bound_variable: BoundVariable, 65 | in_variable: Union[Variable, DerivationTree], 66 | inner_formula: Formula, 67 | ) -> ForallFormula: 68 | return ForallFormula(bound_variable, in_variable, inner_formula) 69 | 70 | 71 | def exists( 72 | bound_variable: BoundVariable, 73 | in_variable: Union[Variable, DerivationTree], 74 | inner_formula: Formula, 75 | ) -> ExistsFormula: 76 | return ExistsFormula(bound_variable, in_variable, inner_formula) 77 | 78 | 79 | def before( 80 | var: Union[Variable, DerivationTree], before_var: Union[Variable, DerivationTree] 81 | ) -> StructuralPredicateFormula: 82 | return StructuralPredicateFormula(BEFORE_PREDICATE, var, before_var) 83 | 84 | 85 | def different_position( 86 | var: Variable | DerivationTree, before_var: Variable | DerivationTree 87 | ) -> StructuralPredicateFormula: 88 | """ 89 | This function returns a formula that is true if the two variables/trees 90 | are in different positions in the derivation tree. 91 | 92 | >>> print(different_position(BoundVariable("x", ""), BoundVariable("y", ""))) 93 | different_position(x, y) 94 | 95 | :param var: The first variable/tree. 96 | :param before_var: The second variable/tree. 97 | :return: A formula that is true if the two variables/trees are in different positions in the derivation tree. 98 | """ 99 | 100 | return StructuralPredicateFormula(DIFFERENT_POSITION_PREDICATE, var, before_var) 101 | 102 | 103 | @cache 104 | def true(): 105 | return SMTFormula(z3.BoolVal(True)) 106 | 107 | 108 | @cache 109 | def false(): 110 | return SMTFormula(z3.BoolVal(False)) 111 | 112 | 113 | def smt_for(formula: z3.BoolRef, *free_variables: Variable) -> SMTFormula: 114 | return SMTFormula(formula, *free_variables) 115 | 116 | 117 | def conjunction(*formulas: Formula) -> Formula: 118 | return functools.reduce(lambda a, b: a & b, formulas, true()) 119 | 120 | 121 | def disjunction(*formulas: Formula) -> Formula: 122 | return functools.reduce(lambda a, b: a | b, formulas, false()) 123 | -------------------------------------------------------------------------------- /src/isla/mexpr_lexer/MexprLexer.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '{' 4 | '[' 5 | null 6 | null 7 | '}' 8 | null 9 | '>' 10 | '<' 11 | null 12 | ']' 13 | null 14 | 15 | token symbolic names: 16 | null 17 | BRAOP 18 | OPTOP 19 | TEXT 20 | NL 21 | BRACL 22 | ID 23 | GT 24 | LT 25 | WS 26 | OPTCL 27 | OPTTXT 28 | 29 | rule names: 30 | BRAOP 31 | OPTOP 32 | TEXT 33 | NL 34 | BRACL 35 | ID 36 | ID_LETTER 37 | DIGIT 38 | GT 39 | LT 40 | WS 41 | OPTCL 42 | OPTTXT 43 | 44 | channel names: 45 | DEFAULT_TOKEN_CHANNEL 46 | HIDDEN 47 | 48 | mode names: 49 | DEFAULT_MODE 50 | VAR_DECL 51 | OPTIONAL 52 | 53 | atn: 54 | [4, 0, 11, 86, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 2, 39, 8, 2, 11, 2, 12, 2, 40, 1, 3, 4, 3, 44, 8, 3, 11, 3, 12, 3, 45, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 5, 5, 57, 8, 5, 10, 5, 12, 5, 60, 9, 5, 1, 6, 3, 6, 63, 8, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 4, 10, 72, 8, 10, 11, 10, 12, 10, 73, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 4, 12, 83, 8, 12, 11, 12, 12, 12, 84, 0, 0, 13, 3, 1, 5, 2, 7, 3, 9, 4, 11, 5, 13, 6, 15, 0, 17, 0, 19, 7, 21, 8, 23, 9, 25, 10, 27, 11, 3, 0, 1, 2, 4, 2, 0, 91, 91, 123, 123, 4, 0, 45, 46, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 93, 93, 87, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 1, 11, 1, 0, 0, 0, 1, 13, 1, 0, 0, 0, 1, 19, 1, 0, 0, 0, 1, 21, 1, 0, 0, 0, 1, 23, 1, 0, 0, 0, 2, 25, 1, 0, 0, 0, 2, 27, 1, 0, 0, 0, 3, 29, 1, 0, 0, 0, 5, 33, 1, 0, 0, 0, 7, 38, 1, 0, 0, 0, 9, 43, 1, 0, 0, 0, 11, 49, 1, 0, 0, 0, 13, 53, 1, 0, 0, 0, 15, 62, 1, 0, 0, 0, 17, 64, 1, 0, 0, 0, 19, 66, 1, 0, 0, 0, 21, 68, 1, 0, 0, 0, 23, 71, 1, 0, 0, 0, 25, 77, 1, 0, 0, 0, 27, 82, 1, 0, 0, 0, 29, 30, 5, 123, 0, 0, 30, 31, 1, 0, 0, 0, 31, 32, 6, 0, 0, 0, 32, 4, 1, 0, 0, 0, 33, 34, 5, 91, 0, 0, 34, 35, 1, 0, 0, 0, 35, 36, 6, 1, 1, 0, 36, 6, 1, 0, 0, 0, 37, 39, 8, 0, 0, 0, 38, 37, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41, 8, 1, 0, 0, 0, 42, 44, 5, 10, 0, 0, 43, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 43, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 6, 3, 2, 0, 48, 10, 1, 0, 0, 0, 49, 50, 5, 125, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 6, 4, 3, 0, 52, 12, 1, 0, 0, 0, 53, 58, 3, 15, 6, 0, 54, 57, 3, 15, 6, 0, 55, 57, 3, 17, 7, 0, 56, 54, 1, 0, 0, 0, 56, 55, 1, 0, 0, 0, 57, 60, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 14, 1, 0, 0, 0, 60, 58, 1, 0, 0, 0, 61, 63, 7, 1, 0, 0, 62, 61, 1, 0, 0, 0, 63, 16, 1, 0, 0, 0, 64, 65, 2, 48, 57, 0, 65, 18, 1, 0, 0, 0, 66, 67, 5, 62, 0, 0, 67, 20, 1, 0, 0, 0, 68, 69, 5, 60, 0, 0, 69, 22, 1, 0, 0, 0, 70, 72, 7, 2, 0, 0, 71, 70, 1, 0, 0, 0, 72, 73, 1, 0, 0, 0, 73, 71, 1, 0, 0, 0, 73, 74, 1, 0, 0, 0, 74, 75, 1, 0, 0, 0, 75, 76, 6, 10, 2, 0, 76, 24, 1, 0, 0, 0, 77, 78, 5, 93, 0, 0, 78, 79, 1, 0, 0, 0, 79, 80, 6, 11, 3, 0, 80, 26, 1, 0, 0, 0, 81, 83, 8, 3, 0, 0, 82, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 82, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, 85, 28, 1, 0, 0, 0, 10, 0, 1, 2, 40, 45, 56, 58, 62, 73, 84, 4, 5, 1, 0, 5, 2, 0, 6, 0, 0, 4, 0, 0] -------------------------------------------------------------------------------- /src/isla/mexpr_lexer/MexprLexer.py: -------------------------------------------------------------------------------- 1 | # Generated from MexprLexer.g4 by ANTLR 4.13.0 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 | def serializedATN(): 12 | return [ 13 | 4,0,11,86,6,-1,6,-1,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4, 14 | 2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12, 15 | 7,12,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,2,4,2,39,8,2,11,2,12,2,40, 16 | 1,3,4,3,44,8,3,11,3,12,3,45,1,3,1,3,1,4,1,4,1,4,1,4,1,5,1,5,1,5, 17 | 5,5,57,8,5,10,5,12,5,60,9,5,1,6,3,6,63,8,6,1,7,1,7,1,8,1,8,1,9,1, 18 | 9,1,10,4,10,72,8,10,11,10,12,10,73,1,10,1,10,1,11,1,11,1,11,1,11, 19 | 1,12,4,12,83,8,12,11,12,12,12,84,0,0,13,3,1,5,2,7,3,9,4,11,5,13, 20 | 6,15,0,17,0,19,7,21,8,23,9,25,10,27,11,3,0,1,2,4,2,0,91,91,123,123, 21 | 4,0,45,46,65,90,95,95,97,122,3,0,9,10,13,13,32,32,1,0,93,93,87,0, 22 | 3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,1,11,1,0,0,0,1,13, 23 | 1,0,0,0,1,19,1,0,0,0,1,21,1,0,0,0,1,23,1,0,0,0,2,25,1,0,0,0,2,27, 24 | 1,0,0,0,3,29,1,0,0,0,5,33,1,0,0,0,7,38,1,0,0,0,9,43,1,0,0,0,11,49, 25 | 1,0,0,0,13,53,1,0,0,0,15,62,1,0,0,0,17,64,1,0,0,0,19,66,1,0,0,0, 26 | 21,68,1,0,0,0,23,71,1,0,0,0,25,77,1,0,0,0,27,82,1,0,0,0,29,30,5, 27 | 123,0,0,30,31,1,0,0,0,31,32,6,0,0,0,32,4,1,0,0,0,33,34,5,91,0,0, 28 | 34,35,1,0,0,0,35,36,6,1,1,0,36,6,1,0,0,0,37,39,8,0,0,0,38,37,1,0, 29 | 0,0,39,40,1,0,0,0,40,38,1,0,0,0,40,41,1,0,0,0,41,8,1,0,0,0,42,44, 30 | 5,10,0,0,43,42,1,0,0,0,44,45,1,0,0,0,45,43,1,0,0,0,45,46,1,0,0,0, 31 | 46,47,1,0,0,0,47,48,6,3,2,0,48,10,1,0,0,0,49,50,5,125,0,0,50,51, 32 | 1,0,0,0,51,52,6,4,3,0,52,12,1,0,0,0,53,58,3,15,6,0,54,57,3,15,6, 33 | 0,55,57,3,17,7,0,56,54,1,0,0,0,56,55,1,0,0,0,57,60,1,0,0,0,58,56, 34 | 1,0,0,0,58,59,1,0,0,0,59,14,1,0,0,0,60,58,1,0,0,0,61,63,7,1,0,0, 35 | 62,61,1,0,0,0,63,16,1,0,0,0,64,65,2,48,57,0,65,18,1,0,0,0,66,67, 36 | 5,62,0,0,67,20,1,0,0,0,68,69,5,60,0,0,69,22,1,0,0,0,70,72,7,2,0, 37 | 0,71,70,1,0,0,0,72,73,1,0,0,0,73,71,1,0,0,0,73,74,1,0,0,0,74,75, 38 | 1,0,0,0,75,76,6,10,2,0,76,24,1,0,0,0,77,78,5,93,0,0,78,79,1,0,0, 39 | 0,79,80,6,11,3,0,80,26,1,0,0,0,81,83,8,3,0,0,82,81,1,0,0,0,83,84, 40 | 1,0,0,0,84,82,1,0,0,0,84,85,1,0,0,0,85,28,1,0,0,0,10,0,1,2,40,45, 41 | 56,58,62,73,84,4,5,1,0,5,2,0,6,0,0,4,0,0 42 | ] 43 | 44 | class MexprLexer(Lexer): 45 | 46 | atn = ATNDeserializer().deserialize(serializedATN()) 47 | 48 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 49 | 50 | VAR_DECL = 1 51 | OPTIONAL = 2 52 | 53 | BRAOP = 1 54 | OPTOP = 2 55 | TEXT = 3 56 | NL = 4 57 | BRACL = 5 58 | ID = 6 59 | GT = 7 60 | LT = 8 61 | WS = 9 62 | OPTCL = 10 63 | OPTTXT = 11 64 | 65 | channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] 66 | 67 | modeNames = [ "DEFAULT_MODE", "VAR_DECL", "OPTIONAL" ] 68 | 69 | literalNames = [ "", 70 | "'{'", "'['", "'}'", "'>'", "'<'", "']'" ] 71 | 72 | symbolicNames = [ "", 73 | "BRAOP", "OPTOP", "TEXT", "NL", "BRACL", "ID", "GT", "LT", "WS", 74 | "OPTCL", "OPTTXT" ] 75 | 76 | ruleNames = [ "BRAOP", "OPTOP", "TEXT", "NL", "BRACL", "ID", "ID_LETTER", 77 | "DIGIT", "GT", "LT", "WS", "OPTCL", "OPTTXT" ] 78 | 79 | grammarFileName = "MexprLexer.g4" 80 | 81 | def __init__(self, input=None, output:TextIO = sys.stdout): 82 | super().__init__(input, output) 83 | self.checkVersion("4.13.0") 84 | self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) 85 | self._actions = None 86 | self._predicates = None 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/isla/mexpr_lexer/MexprLexer.tokens: -------------------------------------------------------------------------------- 1 | BRAOP=1 2 | OPTOP=2 3 | TEXT=3 4 | NL=4 5 | BRACL=5 6 | ID=6 7 | GT=7 8 | LT=8 9 | WS=9 10 | OPTCL=10 11 | OPTTXT=11 12 | '{'=1 13 | '['=2 14 | '}'=5 15 | '>'=7 16 | '<'=8 17 | ']'=10 18 | -------------------------------------------------------------------------------- /src/isla/mexpr_lexer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rindPHI/isla/1a04b7833d2960cffe037fb88826111769fefbeb/src/isla/mexpr_lexer/__init__.py -------------------------------------------------------------------------------- /src/isla/mexpr_parser/MexprParser.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | '{' 4 | '[' 5 | null 6 | null 7 | '}' 8 | null 9 | '>' 10 | '<' 11 | null 12 | ']' 13 | null 14 | 15 | token symbolic names: 16 | null 17 | BRAOP 18 | OPTOP 19 | TEXT 20 | NL 21 | BRACL 22 | ID 23 | GT 24 | LT 25 | WS 26 | OPTCL 27 | OPTTXT 28 | 29 | rule names: 30 | matchExpr 31 | matchExprElement 32 | varType 33 | 34 | 35 | atn: 36 | [4, 1, 11, 27, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 1, 0, 4, 0, 8, 8, 0, 11, 0, 12, 0, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 21, 8, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 0, 0, 3, 0, 2, 4, 0, 0, 26, 0, 7, 1, 0, 0, 0, 2, 20, 1, 0, 0, 0, 4, 22, 1, 0, 0, 0, 6, 8, 3, 2, 1, 0, 7, 6, 1, 0, 0, 0, 8, 9, 1, 0, 0, 0, 9, 7, 1, 0, 0, 0, 9, 10, 1, 0, 0, 0, 10, 1, 1, 0, 0, 0, 11, 12, 5, 1, 0, 0, 12, 13, 3, 4, 2, 0, 13, 14, 5, 6, 0, 0, 14, 15, 5, 5, 0, 0, 15, 21, 1, 0, 0, 0, 16, 17, 5, 2, 0, 0, 17, 18, 5, 11, 0, 0, 18, 21, 5, 10, 0, 0, 19, 21, 5, 3, 0, 0, 20, 11, 1, 0, 0, 0, 20, 16, 1, 0, 0, 0, 20, 19, 1, 0, 0, 0, 21, 3, 1, 0, 0, 0, 22, 23, 5, 8, 0, 0, 23, 24, 5, 6, 0, 0, 24, 25, 5, 7, 0, 0, 25, 5, 1, 0, 0, 0, 2, 9, 20] -------------------------------------------------------------------------------- /src/isla/mexpr_parser/MexprParser.tokens: -------------------------------------------------------------------------------- 1 | BRAOP=1 2 | OPTOP=2 3 | TEXT=3 4 | NL=4 5 | BRACL=5 6 | ID=6 7 | GT=7 8 | LT=8 9 | WS=9 10 | OPTCL=10 11 | OPTTXT=11 12 | '{'=1 13 | '['=2 14 | '}'=5 15 | '>'=7 16 | '<'=8 17 | ']'=10 18 | -------------------------------------------------------------------------------- /src/isla/mexpr_parser/MexprParserListener.py: -------------------------------------------------------------------------------- 1 | # Generated from MexprParser.g4 by ANTLR 4.13.0 2 | from antlr4 import * 3 | if "." in __name__: 4 | from .MexprParser import MexprParser 5 | else: 6 | from MexprParser import MexprParser 7 | 8 | # This class defines a complete listener for a parse tree produced by MexprParser. 9 | class MexprParserListener(ParseTreeListener): 10 | 11 | # Enter a parse tree produced by MexprParser#matchExpr. 12 | def enterMatchExpr(self, ctx:MexprParser.MatchExprContext): 13 | pass 14 | 15 | # Exit a parse tree produced by MexprParser#matchExpr. 16 | def exitMatchExpr(self, ctx:MexprParser.MatchExprContext): 17 | pass 18 | 19 | 20 | # Enter a parse tree produced by MexprParser#MatchExprVar. 21 | def enterMatchExprVar(self, ctx:MexprParser.MatchExprVarContext): 22 | pass 23 | 24 | # Exit a parse tree produced by MexprParser#MatchExprVar. 25 | def exitMatchExprVar(self, ctx:MexprParser.MatchExprVarContext): 26 | pass 27 | 28 | 29 | # Enter a parse tree produced by MexprParser#MatchExprOptional. 30 | def enterMatchExprOptional(self, ctx:MexprParser.MatchExprOptionalContext): 31 | pass 32 | 33 | # Exit a parse tree produced by MexprParser#MatchExprOptional. 34 | def exitMatchExprOptional(self, ctx:MexprParser.MatchExprOptionalContext): 35 | pass 36 | 37 | 38 | # Enter a parse tree produced by MexprParser#MatchExprChars. 39 | def enterMatchExprChars(self, ctx:MexprParser.MatchExprCharsContext): 40 | pass 41 | 42 | # Exit a parse tree produced by MexprParser#MatchExprChars. 43 | def exitMatchExprChars(self, ctx:MexprParser.MatchExprCharsContext): 44 | pass 45 | 46 | 47 | # Enter a parse tree produced by MexprParser#varType. 48 | def enterVarType(self, ctx:MexprParser.VarTypeContext): 49 | pass 50 | 51 | # Exit a parse tree produced by MexprParser#varType. 52 | def exitVarType(self, ctx:MexprParser.VarTypeContext): 53 | pass 54 | 55 | 56 | 57 | del MexprParser -------------------------------------------------------------------------------- /src/isla/mexpr_parser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rindPHI/isla/1a04b7833d2960cffe037fb88826111769fefbeb/src/isla/mexpr_parser/__init__.py -------------------------------------------------------------------------------- /src/isla/mexprlexer.tokens: -------------------------------------------------------------------------------- 1 | BRAOP=1 2 | OPTOP=2 3 | TEXT=3 4 | NL=4 5 | BRACL=5 6 | ID=6 7 | GT=7 8 | LT=8 9 | WS=9 10 | OPTCL=10 11 | OPTTXT=11 12 | '{'=1 13 | '['=2 14 | '}'=5 15 | '>'=7 16 | '<'=8 17 | ']'=10 18 | -------------------------------------------------------------------------------- /src/isla/mutator.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import random 20 | from typing import Tuple, Callable, Optional 21 | 22 | from grammar_graph import gg 23 | from returns.functions import tap 24 | from returns.maybe import Nothing, Some 25 | from returns.result import safe, Success 26 | 27 | from isla.derivation_tree import DerivationTree 28 | from isla.existential_helpers import paths_between, path_to_tree 29 | from isla.fuzzer import GrammarCoverageFuzzer 30 | from isla.helpers import ( 31 | Maybe, 32 | parent_or_child, 33 | canonical, 34 | ) 35 | from isla.type_defs import Grammar, Path 36 | 37 | 38 | class Mutator: 39 | def __init__( 40 | self, 41 | grammar: Grammar, 42 | min_mutations: int = 2, 43 | max_mutations: int = 5, 44 | graph: Optional[gg.GrammarGraph] = None, 45 | ): 46 | self.fuzzer = GrammarCoverageFuzzer(grammar) 47 | self.graph = graph or gg.GrammarGraph.from_grammar(grammar) 48 | self.canonical_grammar = canonical(grammar) 49 | 50 | self.min_mutations = min_mutations 51 | self.max_mutations = max_mutations 52 | 53 | def __get_mutator(self) -> Callable[[DerivationTree], Maybe[DerivationTree]]: 54 | mutators = [ 55 | self.replace_subtree_randomly, 56 | self.generalize_subtree, 57 | self.swap_subtrees, 58 | ] 59 | 60 | return random.choices( 61 | mutators, 62 | weights=[2**i for i in range(1, len(mutators) + 1)], 63 | k=1, 64 | )[0] 65 | 66 | def mutate(self, inp: DerivationTree) -> DerivationTree: 67 | target_num_mutations = random.randint(self.min_mutations, self.max_mutations) 68 | 69 | applied_mutations = 0 70 | 71 | def inc_applied_mutations(_): 72 | nonlocal applied_mutations 73 | applied_mutations += 1 74 | 75 | while applied_mutations < target_num_mutations: 76 | inp = ( 77 | self.__get_mutator()(inp).map(tap(inc_applied_mutations)).value_or(inp) 78 | ) 79 | 80 | return inp 81 | 82 | def replace_subtree_randomly(self, inp: DerivationTree) -> Maybe[DerivationTree]: 83 | candidate_paths = [ 84 | (path, subtree) for path, subtree in inp.paths() if subtree.children 85 | ] 86 | 87 | num_candidate_paths = len(candidate_paths) 88 | 89 | # Decrease weights for paths with many children: Prefer local mutations. 90 | path, subtree = random.choices( 91 | candidate_paths, 92 | weights=[ 93 | 1 94 | + num_candidate_paths 95 | - len( 96 | [ 97 | other_path 98 | for other_path, _ in candidate_paths 99 | if path == other_path[: len(path)] 100 | ] 101 | ) 102 | for path, _ in candidate_paths 103 | ], 104 | k=1, 105 | )[0] 106 | 107 | return Some( 108 | self.fuzzer.expand_tree( 109 | inp.replace_path(path, DerivationTree(subtree.value)) 110 | ) 111 | ) 112 | 113 | def swap_subtrees(self, inp: DerivationTree) -> Maybe[DerivationTree]: 114 | def process( 115 | path_tree_pair: Tuple[ 116 | Tuple[Path, DerivationTree], Tuple[Path, DerivationTree] 117 | ] 118 | ) -> DerivationTree: 119 | (path_1, tree_1), (path_2, tree_2) = path_tree_pair 120 | return inp.replace_path(path_1, tree_2).replace_path(path_2, tree_1) 121 | 122 | return ( 123 | safe( 124 | lambda: random.choice( 125 | [ 126 | ((path_1, tree_1), (path_2, tree_2)) 127 | for path_idx_1, (path_1, tree_1) in enumerate(inp.paths()) 128 | for path_idx_2, (path_2, tree_2) in enumerate(inp.paths()) 129 | if path_idx_1 < path_idx_2 130 | and not parent_or_child(path_1, path_2) 131 | and tree_1.value == tree_2.value 132 | ] 133 | ), 134 | exceptions=(IndexError,), 135 | )() 136 | .map(process) 137 | .map(Some) 138 | .lash(lambda _: Success(Nothing)) 139 | .unwrap() 140 | ) 141 | 142 | def generalize_subtree(self, inp: DerivationTree) -> Maybe[DerivationTree]: 143 | candidate_paths = [ 144 | (path, tree) 145 | for path, tree in inp.paths() 146 | if tree.children and paths_between(self.graph, tree.value, tree.value) 147 | ] 148 | 149 | if not candidate_paths: 150 | return Maybe.nothing() 151 | 152 | path, tree = random.choice(candidate_paths) 153 | self_embedding_tree = random.choice( 154 | path_to_tree( 155 | self.canonical_grammar, 156 | random.choice(paths_between(self.graph, tree.value, tree.value)), 157 | ) 158 | ) 159 | 160 | matching_leaf = random.choice( 161 | [p for p, t in self_embedding_tree.leaves() if t.value == tree.value] 162 | ) 163 | 164 | return Some( 165 | self.fuzzer.expand_tree( 166 | inp.replace_path( 167 | path, self_embedding_tree.replace_path(matching_leaf, tree) 168 | ) 169 | ) 170 | ) 171 | -------------------------------------------------------------------------------- /src/isla/resources/.islarc: -------------------------------------------------------------------------------- 1 | [[defaults.default]] 2 | 3 | "--log-level" = "WARNING" 4 | 5 | [[defaults.create]] 6 | 7 | "--base-name" = "project" 8 | 9 | [[defaults.solve]] 10 | 11 | # "--output-dir" can point to a directory, default (no assignment) is stdout 12 | "--tree" = false 13 | "--pretty-print" = false 14 | "--num-solutions" = 1 15 | "--timeout" = -1 16 | "--unsat-support" = false 17 | "--free-instantiations" = 10 18 | "--smt-instantiations" = 10 19 | "--unique-trees" = false 20 | "--unwinding-depth" = 4 21 | "--weight-vector" = "6.5,1,4,2,19" 22 | "-k" = 3 23 | 24 | [[defaults.fuzz]] 25 | 26 | "--ending" = ".txt" 27 | "--output-dir" = "." 28 | "--num-solutions" = 1 29 | "--timeout" = -1 30 | "--unsat-support" = false 31 | "--free-instantiations" = 10 32 | "--smt-instantiations" = 10 33 | "--unique-trees" = false 34 | "--unwinding-depth" = 4 35 | "--weight-vector" = "7,1,4,2,19" 36 | "-k" = 3 37 | 38 | [[defaults.mutate]] 39 | 40 | # "--output-file" can point to a file, default (no assignment) is stdout 41 | "--min-mutations" = 2 42 | "--max-mutations" = 5 43 | "--timeout" = 1.0 44 | 45 | [[defaults.parse]] 46 | 47 | # "--output-file" can point to a file, default (no assignment) is stdout 48 | "--pretty-print" = true 49 | 50 | [[defaults.repair]] 51 | 52 | # "--output-file" can point to a file, default (no assignment) is stdout 53 | "--timeout" = 3.0 -------------------------------------------------------------------------------- /src/isla/resources/cli_stubs/README.md: -------------------------------------------------------------------------------- 1 | # Inputs to the ISLa Solver CLI 2 | 3 | The ISLa solver/checker CLI accepts multiple grammar and constraint files to generate 4 | or check inputs. In the presence of multiple grammar files, they are merged to a single 5 | grammar; multiple constraints are combined to a conjunction. 6 | 7 | This stub project defines grammar and constraint files for a simple assignment language, 8 | which comprises words like `x := 1 ; y := x`. The project contains the following files: 9 | 10 | - `{grammar_1_file.name}` contains the assignment grammar in 11 | standard [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) notation. 12 | - `{grammar_2_file.name}` specifies the assignment grammar as a Python 13 | program. This permits for more concise notations if you need to include many terminal 14 | values in your grammar. You find more information on this format in the 15 | [Fuzzing Book](https://www.fuzzingbook.org/html/Grammars.html). 16 | - `{constraint_file.name}` contains examples of different types ISLa constraints one 17 | can express over the assignment language. 18 | 19 | All the above files contain comments with additional explanations. 20 | 21 | By running the command 22 | 23 | ```bash 24 | isla solve \ 25 | -s 1 -f 1 -t 10 -n -1 \ 26 | {grammar_1_file.name} \ 27 | {constraint_file.name} 28 | ``` 29 | 30 | you obtain as many solutions to the currently specified (and uncommented) constraints 31 | as the solver can come up with within 10 seconds (because of the `-t 10` argument, 32 | which imposes a 10 seconds timeout). 33 | -------------------------------------------------------------------------------- /src/isla/resources/cli_stubs/constraint.isla: -------------------------------------------------------------------------------- 1 | # In this file, we showcase different constraints for the assignment language. 2 | # You can experiment with them or draw inspiration when specifying your own 3 | # language. To include a constraint, comment the currently included ones out 4 | # and remove the comments for the new constraint. You can also combine different 5 | # constraints with the propositional combinators `and`, `or`, `xor`, ``implies`, 6 | # or `iff`. 7 | 8 | # If you want to learn more about ISLa, you may want to have a look at 9 | # 10 | # - Our [ISLa tutorial](https://www.fuzzingbook.org/beta/html/FuzzingWithConstraints.html) 11 | # - The [ISLa Language Specification](https://rindphi.github.io/isla/islaspec/) 12 | 13 | # ----------- 14 | 15 | # This constraint specifies that all variables occurring on the right-hand side 16 | # of an assignment have to appear in the left-hand side of an *earlier* assignment, 17 | # as in `x := 1 ; y := x`. 18 | 19 | forall assgn_1: 20 | exists assgn_2: ( 21 | before(assgn_2, assgn_1) and 22 | assgn_1.. = assgn_2.) 23 | 24 | # ----------- 25 | 26 | # To limit the variable identifiers to be either "a" or "b," use the following 27 | # constraint: 28 | 29 | # = "a" 30 | 31 | # ----------- 32 | 33 | # The following constraint enforces the existence of *at least one* assignment 34 | # involving a variable "a." 35 | 36 | # exists : = "a" 37 | 38 | # ----------- 39 | 40 | # ISLa supports different propositional cominators. The constraint below enforces 41 | # that either all variables are "a", *or* all variables are "b" or "c". Note that 42 | # quantifiers bind stronger than any combinator; and `and` binds stronger than `or`. 43 | 44 | forall : 45 | = "a" 46 | or 47 | forall : ( 48 | = "b" or 49 | = "c" 50 | ) 51 | 52 | # ----------- 53 | 54 | # We can also use regular expressions (or anything one can do with string variables 55 | # using the [SMT-LIB theory catalog](https://smtlib.cs.uiowa.edu/theories.shtml): 56 | 57 | str.in_re(, re.union(str.to_re("a"), re.union(str.to_re("b"), str.to_re("c")))) 58 | 59 | # ----------- 60 | 61 | # We can enforce the existence of exactly four assignments, using the "semantic 62 | # predicate" `count`: 63 | 64 | # count(, "", "5") 65 | 66 | # ----------- 67 | 68 | # Numeric constraints are no problem, either. Let's say we want all numeric right-hand 69 | # sides to be a multiple of 2: 70 | 71 | # str.to.int() mod 2 = 0 72 | 73 | # Note that in ISLa, all variables are strings, and there is no automatic conversion. 74 | # If you know that a variable (in this case the nonterminal ``) only attains 75 | # numeric values, you can convert it to a number using the `str.to.int` function. This 76 | # term then can participate in numeric operations. 77 | 78 | # ----------- 79 | 80 | # Alternatively, we might desire the existence of two assignment of numeric values 81 | # that add up to 6: 82 | 83 | # exists assgn_1: 84 | # exists assgn_2: ( 85 | # before(assgn_1, assgn_2) and 86 | # str.to.int(assgn_1..) + str.to.int(assgn_2..) = 6) 87 | 88 | # ----------- 89 | 90 | # Or shall all two numbers in assignments add up to 6? 91 | 92 | # forall assgn_1: 93 | # forall assgn_2: ( 94 | # before(assgn_1, assgn_2) implies 95 | # str.to.int(assgn_1..) + str.to.int(assgn_2..) = 6) 96 | -------------------------------------------------------------------------------- /src/isla/resources/cli_stubs/grammar.bnf: -------------------------------------------------------------------------------- 1 | # Below, we define the grammar of the assignment language (with words like `x := a ; z := 1 ; y := y`) 2 | # in [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) notation. Each grammar must contain 3 | # a rule expanding the nonterminal `` to one single other nonterminal. Nonterminals are designated 4 | # by angle brackets; terminals are enclosed in double quotation marks. Quotation marks in terminal symbols 5 | # must be escaped using a backslash, as in `"\""`. Different expansion alternatives are separated by a 6 | # vertical bar `|`. 7 | 8 | ::= 9 | ::= " ; " | 10 | ::= " := " 11 | ::= | 12 | ::= "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" 13 | ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" -------------------------------------------------------------------------------- /src/isla/resources/cli_stubs/grammar.py: -------------------------------------------------------------------------------- 1 | # Here, we define the grammar for the assignment language as a Python program. While 2 | # in general, definitions in BNF might be slightly better readable and have a well-known 3 | # syntax, the Python variant allows creating expansion alternatives programmatically. 4 | # This comes handy, e.g., if you want to include all ASCII lower-case characters, as 5 | # in the example below, without typing them. 6 | # 7 | # Python grammars must assign a variable `grammar` of type `Dict[str, List[str]]`. 8 | # Be aware that the ISLa CLI *executes* Python grammar files; consequently, make sure 9 | # that no harmful code is included. 10 | 11 | import string 12 | from typing import List, Dict 13 | 14 | grammar: Dict[str, List[str]] = { 15 | "": [""], 16 | "": [" ; ", ""], 17 | "": [" := "], 18 | "": ["", ""], 19 | "": list(string.ascii_lowercase), 20 | "": list(string.digits), 21 | } 22 | -------------------------------------------------------------------------------- /src/isla/three_valued_truth.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | from dataclasses import dataclass 20 | from typing import Iterable 21 | 22 | 23 | @dataclass(frozen=True) 24 | class ThreeValuedTruth: 25 | val: int 26 | 27 | FALSE = 0 28 | TRUE = 1 29 | UNKNOWN = 2 30 | 31 | def to_bool(self) -> bool: 32 | assert self.val != ThreeValuedTruth.UNKNOWN 33 | return bool(self.val) 34 | 35 | def __bool__(self): 36 | return self.to_bool() 37 | 38 | def is_false(self): 39 | return self.val == ThreeValuedTruth.FALSE 40 | 41 | def is_true(self): 42 | return self.val == ThreeValuedTruth.TRUE 43 | 44 | def is_unknown(self): 45 | return self.val == ThreeValuedTruth.UNKNOWN 46 | 47 | @staticmethod 48 | def from_bool(b: bool) -> "ThreeValuedTruth": 49 | return ThreeValuedTruth(int(b)) 50 | 51 | @staticmethod 52 | def all(args: Iterable["ThreeValuedTruth"]) -> "ThreeValuedTruth": 53 | args = list(args) 54 | if any(elem.is_false() for elem in args): 55 | return ThreeValuedTruth.false() 56 | if any(elem.is_unknown() for elem in args): 57 | return ThreeValuedTruth.unknown() 58 | return ThreeValuedTruth.true() 59 | 60 | @staticmethod 61 | def any(args: Iterable["ThreeValuedTruth"]) -> "ThreeValuedTruth": 62 | args = list(args) 63 | if any(elem.is_true() for elem in args): 64 | return ThreeValuedTruth.true() 65 | if any(elem.is_unknown() for elem in args): 66 | return ThreeValuedTruth.unknown() 67 | return ThreeValuedTruth.false() 68 | 69 | @staticmethod 70 | def not_(arg: "ThreeValuedTruth") -> "ThreeValuedTruth": 71 | if arg.is_true(): 72 | return ThreeValuedTruth.false() 73 | if arg.is_false(): 74 | return ThreeValuedTruth.true() 75 | return ThreeValuedTruth.unknown() 76 | 77 | @staticmethod 78 | def true(): 79 | return ThreeValuedTruth(ThreeValuedTruth.TRUE) 80 | 81 | @staticmethod 82 | def false(): 83 | return ThreeValuedTruth(ThreeValuedTruth.FALSE) 84 | 85 | @staticmethod 86 | def unknown(): 87 | return ThreeValuedTruth(ThreeValuedTruth.UNKNOWN) 88 | 89 | def __neg__(self): 90 | if self.is_unknown(): 91 | return self 92 | 93 | return ThreeValuedTruth(not bool(self)) 94 | 95 | def __and__(self, other: "ThreeValuedTruth") -> "ThreeValuedTruth": 96 | if self.is_unknown() or other.is_unknown(): 97 | return ThreeValuedTruth.unknown() 98 | 99 | return ThreeValuedTruth.from_bool(bool(self) and bool(other)) 100 | 101 | def __or__(self, other: "ThreeValuedTruth") -> "ThreeValuedTruth": 102 | if self.is_unknown() or other.is_unknown(): 103 | return ThreeValuedTruth.unknown() 104 | 105 | return ThreeValuedTruth.from_bool(bool(self) or bool(other)) 106 | 107 | def __str__(self): 108 | return "TRUE" if self.is_true() else "FALSE" if self.is_false() else "UNKNOWN" 109 | -------------------------------------------------------------------------------- /src/isla/trie.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | from typing import List, Dict, Optional, Tuple, Generic, TypeVar 20 | 21 | import datrie 22 | 23 | from isla.helpers import is_path 24 | from isla.type_defs import Path 25 | 26 | T = TypeVar("T") 27 | 28 | 29 | class SubtreesTrie(Generic[T]): 30 | def __init__( 31 | self, 32 | init_map: Optional[Dict[Path, Tuple[Path, T]]] = None, 33 | init_trie: Optional[datrie.Trie] = None, 34 | root_path: Optional[Path] = None, 35 | ): 36 | if init_trie: 37 | self.trie = init_trie 38 | else: 39 | self.trie = datrie.Trie([chr(i) for i in range(30)]) 40 | for path in init_map or {}: 41 | self.trie[path_to_trie_key(path)] = init_map[path] 42 | 43 | if root_path is not None: 44 | self.root_path: str = path_to_trie_key(root_path) 45 | else: 46 | self.root_path: str = "" 47 | 48 | def __setitem__(self, key: Path, value: Tuple[Path, T]): 49 | assert is_path(key) 50 | self.trie[path_to_trie_key(key)] = value 51 | 52 | def __getitem__(self, item: Path) -> Tuple[Path, T]: 53 | assert is_path(item) 54 | return self.trie[path_to_trie_key(item)] 55 | 56 | def keys(self) -> List[Path]: 57 | return [ 58 | trie_key_to_path(chr(1) + suffix) 59 | for suffix in self.trie.suffixes(self.root_path) 60 | ] 61 | 62 | def values(self) -> List[Tuple[Path, T]]: 63 | return [ 64 | ( 65 | value := self.trie[self.root_path + suffix], 66 | (value[0][len(self.root_path) - 1 :], value[1]), 67 | )[-1] 68 | for suffix in self.trie.suffixes(self.root_path) 69 | ] 70 | 71 | def items(self) -> List: 72 | return [ 73 | ( 74 | trie_key_to_path(chr(1) + suffix), 75 | ( 76 | value := self.trie[self.root_path + suffix], 77 | (value[0][len(self.root_path) - 1 :], value[1]), 78 | )[-1], 79 | ) 80 | for suffix in self.trie.suffixes(self.root_path) 81 | ] 82 | 83 | def get_subtrie(self, new_root_path: Path) -> "SubtreesTrie": 84 | assert is_path(new_root_path) 85 | return SubtreesTrie(init_trie=self.trie, root_path=new_root_path) 86 | 87 | 88 | def path_to_trie_key(path: Path) -> str: 89 | # 0-bytes are ignored by the trie ==> +1 90 | # To represent the empty part, reserve chr(1) ==> +2 91 | if not path: 92 | return chr(1) 93 | 94 | return chr(1) + "".join([chr(i + 2) for i in path]) 95 | 96 | 97 | def trie_key_to_path(key: str) -> Path: 98 | if not key or key[0] != chr(1): 99 | raise RuntimeError( 100 | f"Invalid trie key '{key}' ({[ord(c) for c in key]}), should start with 1" 101 | ) 102 | 103 | if key == chr(1): 104 | return () 105 | 106 | return tuple([ord(c) - 2 for c in key if ord(c) != 1]) 107 | -------------------------------------------------------------------------------- /src/isla/type_defs.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | from typing import Tuple, Optional, List, TypeVar, TypeAlias, Mapping, Sequence 20 | 21 | from frozendict import frozendict 22 | 23 | S = TypeVar("S") 24 | T = TypeVar("T") 25 | 26 | ImmutableList: TypeAlias = tuple[T, ...] 27 | Pair: TypeAlias = Tuple[S, T] 28 | 29 | ParseTree = Tuple[str, Optional[List["ParseTree"]]] 30 | Path = Tuple[int, ...] 31 | 32 | Grammar = Mapping[str, Sequence[str]] 33 | CanonicalGrammar = Mapping[str, Sequence[Sequence[str]]] 34 | 35 | FrozenGrammar = frozendict[str, Tuple[str, ...]] 36 | FrozenCanonicalGrammar = frozendict[str, Tuple[Tuple[str, ...], ...]] 37 | 38 | # DEPRECATED! # TODO remove and replace with FrozenGrammar 39 | ImmutableGrammar = Tuple[Tuple[str, Tuple[str, ...]], ...] 40 | -------------------------------------------------------------------------------- /src/isla_formalizations/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | -------------------------------------------------------------------------------- /src/isla_formalizations/csv.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import string 20 | import subprocess 21 | import tempfile 22 | from typing import Union 23 | 24 | import isla.derivation_tree 25 | from isla.helpers import srange 26 | from isla.isla_predicates import COUNT_PREDICATE 27 | from isla.language import parse_isla 28 | 29 | CSV_GRAMMAR = { 30 | "": [""], 31 | "": [""], 32 | "": [""], 33 | "": ["", ""], 34 | "": ["\n"], 35 | "": ["", ";"], 36 | "": ["", ""], 37 | "": [""], 38 | "": [ 39 | "", 40 | "", 41 | ], 42 | "": [ 43 | c 44 | for c in srange(string.printable) 45 | if c not in ["\n", ";", '"', " ", "\t", "\r", '"'] 46 | ], 47 | "": ['""'], 48 | "": [""], 49 | "": ["", ""], 50 | "": [c for c in srange(string.printable) if c not in ['"']], 51 | "": ["", " "], 52 | } 53 | 54 | CSV_HEADERBODY_GRAMMAR = { 55 | "": [""], 56 | "": [""], 57 | "": [""], 58 | "": [""], 59 | "": ["", ""], 60 | "": ["\n"], 61 | "": ["", ";"], 62 | "": ["", ""], 63 | "": [""], 64 | "": [ 65 | "", 66 | "", 67 | ], 68 | "": [ 69 | c 70 | for c in srange(string.printable) 71 | if c not in ["\n", ";", '"', " ", "\t", "\r", '"'] 72 | ], 73 | "": ['""'], 74 | "": [""], 75 | "": ["", ""], 76 | "": [c for c in srange(string.printable) if c not in ['"']], 77 | "": ["", " "], 78 | } 79 | 80 | 81 | def csv_lint(tree: isla.derivation_tree.DerivationTree) -> Union[bool, str]: 82 | with tempfile.NamedTemporaryFile(suffix=".csv") as tmp: 83 | tmp.write(str(tree).encode()) 84 | tmp.flush() 85 | # csvlint from https://github.com/Clever/csvlint/releases 86 | cmd = ["csvlint", "-delimiter", ";", tmp.name] 87 | process = subprocess.Popen(cmd, stderr=subprocess.PIPE) 88 | (stdout, stderr) = process.communicate() 89 | exit_code = process.wait() 90 | 91 | err_msg = stderr.decode("utf-8") 92 | 93 | has_error = exit_code != 0 or (bool(err_msg) and not "valid" in err_msg) 94 | 95 | if has_error: 96 | print(err_msg) 97 | 98 | return True if not has_error else err_msg 99 | 100 | 101 | # csv_colno_property = """ 102 | # forall hline in start: 103 | # exists int colno: 104 | # ((>= (str.to.int colno) 3) and 105 | # ((<= (str.to.int colno) 5) and 106 | # (count(hline, "", colno) and 107 | # forall line in start: 108 | # count(line, "", colno))))""" 109 | 110 | csv_colno_property = """ 111 | exists int num: 112 | forall elem in start: 113 | (str.to.int(num) >= 1 and 114 | count(elem, "", num))""" 115 | 116 | CSV_COLNO_PROPERTY = parse_isla( 117 | csv_colno_property, CSV_GRAMMAR, semantic_predicates={COUNT_PREDICATE} 118 | ) 119 | -------------------------------------------------------------------------------- /src/isla_formalizations/scriptsizec.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import string 20 | import subprocess 21 | import tempfile 22 | from subprocess import PIPE 23 | from typing import Union 24 | 25 | import isla.derivation_tree 26 | 27 | # Based on: 28 | # Kartik Talwar. Tiny-C Compiler. https://gist.github.com/KartikTalwar/3095780. 29 | from isla.helpers import srange 30 | from isla.isla_predicates import ( 31 | BEFORE_PREDICATE, 32 | SAME_POSITION_PREDICATE, 33 | LEVEL_PREDICATE, 34 | ) 35 | from isla.language import parse_isla 36 | 37 | SCRIPTSIZE_C_GRAMMAR = { 38 | "": [""], 39 | "": [ 40 | "", 41 | "if else ", 42 | "if ", 43 | "while ", 44 | "do while;", 45 | ";", 46 | ";", 47 | ], 48 | "": ["{}"], 49 | "": ["", ""], 50 | "": ["", ""], 51 | "": ["int = ;", "int ;"], 52 | "": ["()"], 53 | "": [ 54 | " = ", 55 | "", 56 | ], 57 | "": [ 58 | " < ", 59 | "", 60 | ], 61 | "": [ 62 | " + ", 63 | " - ", 64 | "", 65 | ], 66 | "": [ 67 | "", 68 | "", 69 | "", 70 | ], 71 | "": srange(string.ascii_lowercase), 72 | "": [ 73 | "", 74 | "", 75 | ], 76 | "": [ 77 | "", 78 | "", 79 | ], 80 | "": srange(string.digits), 81 | "": list(set(srange(string.digits)) - {"0"}), 82 | } 83 | 84 | # Forall s use_id in any expression (i.e., only RHSs), 85 | # there must be a decl, 86 | # which occurs before use_id and on the same or a higher level, 87 | # that assigns use_id a value. 88 | SCRIPTSIZE_C_DEF_USE_CONSTR_TEXT = """ 89 | forall expr in start: 90 | forall use_id in expr: 91 | exists decl="int { def_id}[ = ];" in start: 92 | (level("GE", "", decl, expr) and 93 | (before(decl, expr) and 94 | (= use_id def_id))) 95 | """ 96 | 97 | SCRIPTSIZE_C_DEF_USE_CONSTR = parse_isla( 98 | SCRIPTSIZE_C_DEF_USE_CONSTR_TEXT, 99 | SCRIPTSIZE_C_GRAMMAR, 100 | structural_predicates={BEFORE_PREDICATE, LEVEL_PREDICATE}, 101 | ) 102 | 103 | # TODO: Scoping! 104 | SCRIPTSIZE_C_NO_REDEF_TEXT = """ 105 | forall declaration="int { def_id}[ = ];" in start: 106 | forall other_declaration="int { other_def_id}[ = ];" in start: 107 | (same_position(declaration, other_declaration) xor not (= def_id other_def_id))""" 108 | 109 | SCRIPTSIZE_C_NO_REDEF_CONSTR = parse_isla( 110 | SCRIPTSIZE_C_NO_REDEF_TEXT, 111 | SCRIPTSIZE_C_GRAMMAR, 112 | structural_predicates={SAME_POSITION_PREDICATE}, 113 | ) 114 | 115 | 116 | def compile_scriptsizec_clang( 117 | tree: isla.derivation_tree.DerivationTree, 118 | ) -> Union[bool, str]: 119 | contents = "int main() {\n" 120 | contents += "\n" + str(tree).replace("\n", " \t") 121 | contents += "\n" + "}" 122 | 123 | with tempfile.NamedTemporaryFile(suffix=".c") as tmp, tempfile.NamedTemporaryFile( 124 | suffix=".o" 125 | ) as outfile: 126 | tmp.write(contents.encode()) 127 | tmp.flush() 128 | cmd = ["clang", tmp.name, "-o", outfile.name] 129 | process = subprocess.Popen(cmd, stderr=PIPE) 130 | (stdout, stderr) = process.communicate() 131 | exit_code = process.wait() 132 | 133 | err_msg = stderr.decode("utf-8") 134 | has_error = exit_code != 0 or (bool(err_msg) and "error" in err_msg) 135 | 136 | return True if not has_error else err_msg 137 | -------------------------------------------------------------------------------- /src/isla_formalizations/simple_tar.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import copy 20 | import string 21 | from typing import cast, Union, List 22 | 23 | import z3 24 | from grammar_graph import gg 25 | 26 | import isla.derivation_tree 27 | import isla.isla_shortcuts as sc 28 | from isla import language 29 | from isla.helpers import delete_unreachable, srange 30 | from isla.parser import EarleyParser 31 | from isla.z3_helpers import z3_eq 32 | from .tar import ljust_crop_tar, rjust_crop_tar 33 | 34 | SIMPLE_TAR_GRAMMAR = { 35 | "": [""], 36 | "": ["", ""], 37 | "": ["
"], 38 | "
": ["" "" "" ""], 39 | "": [""], 40 | "": [ 41 | "", 42 | "", 43 | ], 44 | "": [""], 45 | "": ["0", "2"], # Generalize? # normal file # symbolic link 46 | "": ["", ""], 47 | "": ["CONTENT"], 48 | "": ["", ""], 49 | "": srange("01234567"), 50 | "": srange(string.ascii_letters + string.digits + "_"), 51 | "": ["", ""], 52 | "": list( 53 | set(srange(string.printable)) - set(srange(string.whitespace + "\b\f\v")) 54 | ), 55 | "": ["", ""], 56 | "": ["", ""], 57 | "": ["\x00"], 58 | "": [" "], 59 | } 60 | 61 | 62 | def tar_checksum( 63 | graph: gg.GrammarGraph, 64 | header: isla.derivation_tree.DerivationTree, 65 | checksum_tree: isla.derivation_tree.DerivationTree, 66 | ) -> language.SemPredEvalResult: 67 | if not header.is_complete(): 68 | return language.SemPredEvalResult(None) 69 | 70 | current_checksum_path = header.find_node(checksum_tree) 71 | 72 | checksum_grammar = copy.deepcopy(SIMPLE_TAR_GRAMMAR) 73 | checksum_grammar[""] = [""] 74 | checksum_grammar[""] = [ 75 | "" 76 | ] 77 | checksum_grammar = delete_unreachable(checksum_grammar) 78 | checksum_parser = EarleyParser(checksum_grammar) 79 | 80 | space_checksum = isla.derivation_tree.DerivationTree.from_parse_tree( 81 | next(checksum_parser.parse(" ")) 82 | ).get_subtree((0,)) 83 | header_wo_checksum = header.replace_path(current_checksum_path, space_checksum) 84 | 85 | header_bytes: List[int] = list(str(header_wo_checksum).encode("ascii")) 86 | 87 | checksum_value = str(oct(sum(header_bytes)))[2:].rjust(6, "0") + "\x00 " 88 | 89 | checksum_grammar = copy.deepcopy(SIMPLE_TAR_GRAMMAR) 90 | checksum_grammar[""] = [""] 91 | checksum_grammar = delete_unreachable(checksum_grammar) 92 | checksum_parser = EarleyParser(checksum_grammar) 93 | 94 | new_checksum_tree = isla.derivation_tree.DerivationTree.from_parse_tree( 95 | list(checksum_parser.parse(checksum_value))[0] 96 | ).get_subtree((0,)) 97 | 98 | if str(new_checksum_tree) == str(checksum_tree): 99 | return language.SemPredEvalResult(True) 100 | 101 | return language.SemPredEvalResult({checksum_tree: new_checksum_tree}) 102 | 103 | 104 | TAR_CHECKSUM_PREDICATE = language.SemanticPredicate( 105 | "tar_checksum", 2, tar_checksum, binds_tree=False 106 | ) 107 | 108 | 109 | def tar_checksum( 110 | header: Union[language.Variable, isla.derivation_tree.DerivationTree], 111 | checksum: Union[language.Variable, isla.derivation_tree.DerivationTree], 112 | ) -> language.SemanticPredicateFormula: 113 | return language.SemanticPredicateFormula(TAR_CHECKSUM_PREDICATE, header, checksum) 114 | 115 | 116 | mgr = language.VariableManager(SIMPLE_TAR_GRAMMAR) 117 | start = mgr.const("$start", "") 118 | 119 | link_constraint = sc.forall( 120 | mgr.bv("$entry", ""), 121 | start, 122 | sc.forall( 123 | mgr.bv("$typeflag", ""), 124 | mgr.bv("$entry"), 125 | mgr.smt( 126 | cast(z3.BoolRef, z3_eq(mgr.bv("$typeflag").to_smt(), z3.StringVal("0"))) 127 | ) 128 | | ( 129 | mgr.smt(z3_eq(mgr.bv("$typeflag").to_smt(), z3.StringVal("2"))) 130 | & sc.forall_bind( 131 | mgr.bv("$linked_file_name_str", "") + "", 132 | mgr.bv("$linked_file_name", ""), 133 | mgr.bv("$entry"), 134 | sc.exists( 135 | mgr.bv("$linked_entry", ""), 136 | start, 137 | ( 138 | sc.before(mgr.bv("$entry"), mgr.bv("$linked_entry")) 139 | | sc.before(mgr.bv("$linked_entry"), mgr.bv("$entry")) 140 | ) 141 | & sc.forall_bind( 142 | mgr.bv("$file_name_str", "") + "", 143 | mgr.bv("$file_name"), 144 | mgr.bv("$linked_entry"), 145 | mgr.smt( 146 | z3_eq( 147 | mgr.bv("$file_name_str").to_smt(), 148 | mgr.bv("$linked_file_name_str").to_smt(), 149 | ) 150 | ), 151 | ), 152 | ), 153 | ) 154 | ), 155 | ), 156 | ) 157 | 158 | file_name_length_constraint = sc.forall( 159 | mgr.bv("$file_name", ""), 160 | start, 161 | ljust_crop_tar(mgr.bv("$file_name"), 100, "\x00"), 162 | ) 163 | 164 | linked_file_name_length_constraint = sc.forall( 165 | mgr.bv("$linked_file_name", ""), 166 | start, 167 | ljust_crop_tar(mgr.bv("$linked_file_name"), 100, "\x00"), 168 | ) 169 | 170 | checksum_length_constraint = sc.forall( 171 | mgr.bv("$checksum", ""), 172 | start, 173 | rjust_crop_tar(mgr.bv("$checksum"), 8, "0"), 174 | ) 175 | 176 | checksum_constraint = sc.forall( 177 | mgr.bv("$header", "
"), 178 | start, 179 | sc.forall( 180 | mgr.bv("$checksum", ""), 181 | mgr.bv("$header"), 182 | tar_checksum(mgr.bv("$header"), mgr.bv("$checksum")), 183 | ), 184 | ) 185 | 186 | TAR_CONSTRAINTS = mgr.create( 187 | file_name_length_constraint 188 | & checksum_constraint 189 | & checksum_length_constraint 190 | & linked_file_name_length_constraint 191 | & link_constraint 192 | # sc.forall( 193 | # mgr.bv("$entry", ""), 194 | # start, 195 | # sc.forall( 196 | # mgr.bv("$typeflag", ""), 197 | # mgr.bv("$entry"), 198 | # mgr.smt(cast(z3.BoolRef, mgr.bv("$typeflag").to_smt() == z3.StringVal("0"))) 199 | # | (mgr.smt(mgr.bv("$typeflag").to_smt() == z3.StringVal("2")) & 200 | # sc.forall_bind( 201 | # mgr.bv("$linked_file_name_chars", "") + "", 202 | # mgr.bv("$linked_file_name", ""), 203 | # mgr.bv("$entry"), 204 | # sc.exists( 205 | # mgr.bv("$linked_entry", ""), 206 | # start, 207 | # (sc.before(mgr.bv("$entry"), mgr.bv("$linked_entry")) 208 | # | sc.before(mgr.bv("$linked_entry"), mgr.bv("$entry"))) & 209 | # sc.forall_bind( 210 | # mgr.bv("$file_name_chars", "") + "", 211 | # mgr.bv("$file_name"), 212 | # mgr.bv("$linked_entry"), 213 | # mgr.smt(mgr.bv("$file_name_chars").to_smt() == mgr.bv("$linked_file_name_chars").to_smt()) 214 | # ) 215 | # ))) 216 | # )) 217 | ) 218 | -------------------------------------------------------------------------------- /src/isla_formalizations/xml_lang.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import copy 20 | import string 21 | import xml.etree.ElementTree as ET 22 | from html import escape 23 | from typing import Optional, List 24 | 25 | from isla.helpers import srange 26 | from isla.isla_predicates import IN_TREE_PREDICATE, SAME_POSITION_PREDICATE 27 | from isla.language import parse_isla 28 | from isla.derivation_tree import DerivationTree 29 | 30 | XML_GRAMMAR = { 31 | "": [""], 32 | "": [ 33 | "", 34 | "", 35 | ], 36 | "": [ 37 | "", 38 | "", 39 | "", 40 | ], 41 | "": ["< >", "<>"], 42 | "": ["< />", "</>"], 43 | "": [">"], 44 | "": [" ", '=""'], 45 | "": [ 46 | "", 47 | "", 48 | ], 49 | "": srange("_" + string.ascii_letters), 50 | "": ["", ""], 51 | "": [""] + srange("-." + string.digits), 52 | "": ["", ""], 53 | "": [ 54 | escape(c) 55 | for c in srange(string.ascii_letters + string.digits + "\"'. \t/?-,=:+") 56 | ], 57 | } 58 | 59 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES = copy.deepcopy(XML_GRAMMAR) 60 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES.update( 61 | { 62 | "": [ 63 | "", 64 | "", 65 | ], 66 | "": [ 67 | "", 68 | "", 69 | ], 70 | "": [":"], 71 | } 72 | ) 73 | 74 | 75 | def validate_xml(inp: DerivationTree, out: Optional[List[str]] = None) -> bool: 76 | try: 77 | ET.fromstring(str(inp)) 78 | return True 79 | except Exception as err: 80 | if out is not None: 81 | out.append(str(err)) 82 | return False 83 | 84 | 85 | xml_wellformedness_constraint = """ 86 | forall tree="<{ opid}[ ]> clid}>" in start: 87 | (= opid clid) 88 | """ 89 | 90 | XML_WELLFORMEDNESS_CONSTRAINT = parse_isla( 91 | xml_wellformedness_constraint, XML_GRAMMAR_WITH_NAMESPACE_PREFIXES 92 | ) 93 | 94 | xml_attribute_namespace_constraint = r""" 95 | forall attribute="{ prefix_use}:{ maybe_def}=\"\"": 96 | ((not prefix_use = "xmlns" or maybe_def = "xmlns") implies 97 | exists outer_tag="< { cont_attribute}>>": 98 | (inside(attribute, outer_tag) and 99 | exists def_attribute="xmlns:{ prefix_def}=\"\"" in cont_attribute: 100 | (not (= prefix_def "xmlns") and (= prefix_use prefix_def))))""" 101 | 102 | XML_ATTRIBUTE_NAMESPACE_CONSTRAINT = parse_isla( 103 | xml_attribute_namespace_constraint, 104 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 105 | structural_predicates={IN_TREE_PREDICATE}, 106 | ) 107 | 108 | xml_tag_namespace_constraint = r""" 109 | forall xml_tree="<{ prefix_use}:[ ][/]>[]": 110 | exists outer_tag="< { cont_attribute}>>": 111 | (inside(xml_tree, outer_tag) and 112 | exists ="xmlns:{ prefix_def}=\"\"" in cont_attribute: 113 | prefix_use = prefix_def)""" 114 | 115 | # xml_tag_namespace_constraint = r''' 116 | # forall xml_tree="<{ prefix_use}:[ ][/]>[]": 117 | # exists outer_tag="< { cont_attribute}>>": ( 118 | # inside(xml_tree, outer_tag) and 119 | # exists in cont_attribute: ( 120 | # ...[1] = "xmlns" and 121 | # prefix_use = ...[2]))''' 122 | 123 | XML_TAG_NAMESPACE_CONSTRAINT = parse_isla( 124 | xml_tag_namespace_constraint, 125 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 126 | structural_predicates={IN_TREE_PREDICATE}, 127 | ) 128 | 129 | XML_NAMESPACE_CONSTRAINT = ( 130 | XML_TAG_NAMESPACE_CONSTRAINT & XML_ATTRIBUTE_NAMESPACE_CONSTRAINT 131 | ) 132 | 133 | xml_no_attr_redef_constraint = r""" 134 | forall attr_outer in start: 135 | forall attr_inner_1="{ id_1}=\"\"" in attr_outer: 136 | forall attr_inner_2="{ id_2}=\"\"" in attr_outer: 137 | (same_position(attr_inner_1, attr_inner_2) xor 138 | not (= id_1 id_2))""" 139 | 140 | XML_NO_ATTR_REDEF_CONSTRAINT = parse_isla( 141 | xml_no_attr_redef_constraint, 142 | XML_GRAMMAR_WITH_NAMESPACE_PREFIXES, 143 | structural_predicates={IN_TREE_PREDICATE, SAME_POSITION_PREDICATE}, 144 | ) 145 | -------------------------------------------------------------------------------- /tests/.pytest_cache/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by pytest automatically. 2 | v 3 | -------------------------------------------------------------------------------- /tests/.pytest_cache/CACHEDIR.TAG: -------------------------------------------------------------------------------- 1 | Signature: 8a477f597d28d172789f06886806bc55 2 | # This file is a cache directory tag created by pytest. 3 | # For information about cache directory tags, see: 4 | # http://www.bford.info/cachedir/spec.html 5 | -------------------------------------------------------------------------------- /tests/.pytest_cache/README.md: -------------------------------------------------------------------------------- 1 | # pytest cache directory # 2 | 3 | This directory contains data from the pytest's cache plugin, 4 | which provides the `--lf` and `--ff` options, as well as the `cache` fixture. 5 | 6 | **Do not** commit this to version control. 7 | 8 | See [the docs](https://docs.pytest.org/en/stable/cache.html) for more information. 9 | -------------------------------------------------------------------------------- /tests/test_data.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import string 20 | from typing import Dict, Callable 21 | 22 | from isla import language 23 | from isla.helpers import tree_to_string, srange, crange, convert_ebnf_grammar 24 | from isla.parser import EarleyParser 25 | from isla.type_defs import ParseTree, Path, Grammar 26 | 27 | LANG_GRAMMAR = { 28 | "": 29 | [""], 30 | "": 31 | [" ; ", ""], 32 | "": 33 | [" := "], 34 | "": 35 | ["", ""], 36 | "": list(string.ascii_lowercase), 37 | "": list(string.digits) 38 | } 39 | 40 | CONFIG_GRAMMAR: Grammar = { 41 | "": [""], 42 | "": ["pagesize=\nbufsize="], 43 | "": [""], 44 | "": [""], 45 | "": [""], 46 | "": ["", ""], 47 | "": list("0123456789"), 48 | "": list("123456789"), 49 | } 50 | 51 | SIMPLE_CSV_GRAMMAR = { 52 | "": [""], 53 | "": ["\n"], 54 | "": ["", ""], 55 | "": ["\n"], 56 | "": [";", ""], 57 | "": list(string.ascii_lowercase), 58 | } 59 | 60 | 61 | def eval_lang(inp: str) -> Dict[str, int]: 62 | def assgnlhs(assgn: ParseTree): 63 | return tree_to_string(get_subtree((0,), assgn)) 64 | 65 | def assgnrhs(assgn: ParseTree): 66 | return tree_to_string(get_subtree((2,), assgn)) 67 | 68 | valueMap: Dict[str, int] = {} 69 | tree = list(EarleyParser(LANG_GRAMMAR).parse(inp))[0] 70 | 71 | def evalAssignments(tree): 72 | node, children = tree 73 | if node == "": 74 | lhs = assgnlhs(tree) 75 | rhs = assgnrhs(tree) 76 | if rhs.isdigit(): 77 | valueMap[lhs] = int(rhs) 78 | else: 79 | valueMap[lhs] = valueMap[rhs] 80 | 81 | dfs(tree, evalAssignments) 82 | 83 | return valueMap 84 | 85 | 86 | def validate_lang(inp: language.DerivationTree) -> bool: 87 | try: 88 | eval_lang(str(inp)) 89 | return True 90 | except Exception: 91 | return False 92 | 93 | 94 | def dfs(tree: ParseTree, action: Callable[[ParseTree], None] = print): 95 | node, children = tree 96 | action(tree) 97 | if children is not None: 98 | for child in children: 99 | dfs(child, action) 100 | 101 | 102 | def get_subtree(path: Path, tree: ParseTree) -> ParseTree: 103 | """Access a subtree based on `path` (a list of children numbers)""" 104 | node, children = tree 105 | 106 | if not path: 107 | return tree 108 | 109 | return get_subtree(path[1:], children[path[0]]) 110 | 111 | 112 | CHARACTERS_WITHOUT_QUOTE = ( 113 | string.digits 114 | + string.ascii_letters 115 | + string.punctuation.replace('"', '').replace('\\', '') 116 | + ' ') 117 | 118 | JSON_EBNF_GRAMMAR: Grammar = { 119 | "": [""], 120 | "": [""], 121 | "": [""], 122 | "": ["", "", "", "", 123 | "true", "false", "null", "'; DROP TABLE STUDENTS"], 124 | "": ["{}", "{}"], 125 | "": ["(,)*"], 126 | "": [":"], 127 | "": ["[]", "[]"], 128 | "": ["(,)*"], 129 | "": ['"' + "" + '"'], 130 | "": ["*"], 131 | "": srange(CHARACTERS_WITHOUT_QUOTE), 132 | "": [""], 133 | "": ["", "", "-", "-"], 134 | "": ["+"], 135 | "": ['0', ""], 136 | "": crange('1', '9'), 137 | "": ["", "."], 138 | "": ["", "E", "e"], 139 | "": ["", '+', '-'], 140 | "": [" "] 141 | } 142 | 143 | JSON_GRAMMAR = convert_ebnf_grammar(JSON_EBNF_GRAMMAR) 144 | -------------------------------------------------------------------------------- /tests/test_data/test_simple.tar: -------------------------------------------------------------------------------- 1 | test1.txt000644 000765 000024 00000000006 14304064365 014474 0ustar00dsteinhoefelstaff000000 000000 test1 2 | -------------------------------------------------------------------------------- /tests/test_data/test_with_links.tar: -------------------------------------------------------------------------------- 1 | test1.txt000644 000765 000024 00000000006 14304062306 014465 0ustar00dsteinhoefelstaff000000 000000 test1 2 | test2.txt000644 000765 000024 00000000006 14304062312 014463 0ustar00dsteinhoefelstaff000000 000000 test2 3 | link1.txt000755 000765 000024 00000000000 14304062352 016242 2test1.txtustar00dsteinhoefelstaff000000 000000 link2.txt000755 000765 000024 00000000000 14304062332 016242 2test2.txtustar00dsteinhoefelstaff000000 000000 -------------------------------------------------------------------------------- /tests/test_doctests.py: -------------------------------------------------------------------------------- 1 | import doctest 2 | import logging 3 | import unittest 4 | 5 | from isla import ( 6 | helpers, 7 | cli, 8 | derivation_tree, 9 | evaluator, 10 | existential_helpers, 11 | fuzzer, 12 | isla_predicates, 13 | isla_shortcuts, 14 | language, 15 | optimizer, 16 | mutator, 17 | parser, 18 | performance_evaluator, 19 | three_valued_truth, 20 | solver, 21 | trie, 22 | type_defs, 23 | z3_helpers, 24 | ) 25 | 26 | 27 | class TestDocstrings(unittest.TestCase): 28 | def test_cli(self): 29 | doctest_results = doctest.testmod(m=cli) 30 | self.assertFalse(doctest_results.failed) 31 | 32 | def test_derivation_tree(self): 33 | doctest_results = doctest.testmod(m=derivation_tree) 34 | self.assertFalse(doctest_results.failed) 35 | 36 | def test_evaluator(self): 37 | doctest_results = doctest.testmod(m=evaluator) 38 | self.assertFalse(doctest_results.failed) 39 | 40 | def test_existential_helpers(self): 41 | doctest_results = doctest.testmod(m=existential_helpers) 42 | self.assertFalse(doctest_results.failed) 43 | 44 | def test_fuzzer(self): 45 | doctest_results = doctest.testmod(m=fuzzer) 46 | self.assertFalse(doctest_results.failed) 47 | 48 | def test_helpers(self): 49 | doctest_results = doctest.testmod(m=helpers) 50 | self.assertFalse(doctest_results.failed) 51 | 52 | def test_isla_predicates(self): 53 | doctest_results = doctest.testmod(m=isla_predicates) 54 | self.assertFalse(doctest_results.failed) 55 | 56 | def test_isla_shortcuts(self): 57 | doctest_results = doctest.testmod(m=isla_shortcuts) 58 | self.assertFalse(doctest_results.failed) 59 | 60 | def test_language(self): 61 | doctest_results = doctest.testmod(m=language) 62 | self.assertFalse(doctest_results.failed) 63 | 64 | def test_mutator(self): 65 | doctest_results = doctest.testmod(m=mutator) 66 | self.assertFalse(doctest_results.failed) 67 | 68 | def test_optimizer(self): 69 | doctest_results = doctest.testmod(m=optimizer) 70 | self.assertFalse(doctest_results.failed) 71 | 72 | def test_parser(self): 73 | doctest_results = doctest.testmod(m=parser) 74 | self.assertFalse(doctest_results.failed) 75 | 76 | def test_performance_evaluator(self): 77 | doctest_results = doctest.testmod(m=performance_evaluator) 78 | self.assertFalse(doctest_results.failed) 79 | 80 | def test_solver(self): 81 | logging.getLogger("RegexConverter").setLevel(logging.ERROR) 82 | doctest_results = doctest.testmod(m=solver) 83 | self.assertFalse(doctest_results.failed) 84 | 85 | def test_three_valued_truth(self): 86 | doctest_results = doctest.testmod(m=three_valued_truth) 87 | self.assertFalse(doctest_results.failed) 88 | 89 | def test_trie(self): 90 | doctest_results = doctest.testmod(m=trie) 91 | self.assertFalse(doctest_results.failed) 92 | 93 | def test_type_defs(self): 94 | doctest_results = doctest.testmod(m=type_defs) 95 | self.assertFalse(doctest_results.failed) 96 | 97 | def test_z3_helpers(self): 98 | doctest_results = doctest.testmod(m=z3_helpers) 99 | self.assertFalse(doctest_results.failed) 100 | 101 | 102 | if __name__ == "__main__": 103 | unittest.main() 104 | -------------------------------------------------------------------------------- /tests/test_formalizations.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import os 20 | import pathlib 21 | import unittest 22 | 23 | from grammar_graph import gg 24 | from grammar_graph.gg import path_to_string 25 | 26 | from isla_formalizations.tar import TarParser, TAR_GRAMMAR 27 | 28 | 29 | class TestFormalizations(unittest.TestCase): 30 | def test_tar_parser_with_links(self): 31 | path = os.path.join(os.path.dirname(__file__), 'test_data/' 'test_with_links.tar') 32 | with pathlib.Path(path).open(mode='rb') as asdf: 33 | tar_file_content = asdf.read().decode('ascii') 34 | tree = TarParser().parse(tar_file_content)[0] 35 | 36 | graph = gg.GrammarGraph.from_grammar(TAR_GRAMMAR) 37 | 38 | all_2paths = {path_to_string(p) for p in graph.k_paths(2)} 39 | tree_2paths = {path_to_string(p) for p in graph.k_paths_in_tree(tree, 2)} 40 | self.assertFalse(tree_2paths.difference(all_2paths)) 41 | 42 | self.assertTrue(graph.tree_is_valid(tree)) 43 | 44 | def test_tar_parser_simple_file(self): 45 | path = os.path.join(os.path.dirname(__file__), 'test_data/' 'test_simple.tar') 46 | with pathlib.Path(path).open(mode='rb') as asdf: 47 | tar_file_content = asdf.read().decode('ascii') 48 | tree = TarParser().parse(tar_file_content)[0] 49 | 50 | graph = gg.GrammarGraph.from_grammar(TAR_GRAMMAR) 51 | 52 | all_2paths = {path_to_string(p) for p in graph.k_paths(2)} 53 | tree_2paths = {path_to_string(p) for p in graph.k_paths_in_tree(tree, 2)} 54 | self.assertFalse(tree_2paths.difference(all_2paths)) 55 | 56 | self.assertTrue(graph.tree_is_valid(tree)) 57 | 58 | 59 | if __name__ == '__main__': 60 | unittest.main() 61 | -------------------------------------------------------------------------------- /tests/test_fuzzer.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import logging 20 | import unittest 21 | 22 | from isla.fuzzer import GrammarFuzzer, GrammarCoverageFuzzer 23 | from isla_formalizations import csv as csvlang 24 | 25 | 26 | class TestFuzzer(unittest.TestCase): 27 | def __init__(self, *args): 28 | super().__init__(*args) 29 | self.logger = logging.getLogger("TestFuzzer") 30 | 31 | def test_grammar_fuzzer(self): 32 | grammar = csvlang.CSV_GRAMMAR 33 | fuzzer = GrammarFuzzer(grammar) 34 | for _ in range(100): 35 | try: 36 | self.logger.info(fuzzer.fuzz()) 37 | except: 38 | self.fail() 39 | 40 | def test_grammar_coverage_fuzzer(self): 41 | grammar = csvlang.CSV_GRAMMAR 42 | fuzzer = GrammarCoverageFuzzer(grammar) 43 | for _ in range(100): 44 | try: 45 | self.logger.info(fuzzer.fuzz()) 46 | except: 47 | self.fail() 48 | 49 | 50 | if __name__ == '__main__': 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /tests/test_mutator.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import unittest 20 | 21 | from grammar_graph import gg 22 | from returns.pipeline import is_successful 23 | 24 | from isla.derivation_tree import DerivationTree 25 | from isla.fuzzer import GrammarCoverageFuzzer 26 | from isla.mutator import Mutator 27 | from isla.parser import EarleyParser 28 | from isla.type_defs import Grammar 29 | from test_data import LANG_GRAMMAR 30 | 31 | 32 | class TestMutator(unittest.TestCase): 33 | def test_replace_subtree_randomly(self): 34 | mutator = Mutator(LANG_GRAMMAR) 35 | fuzzer = GrammarCoverageFuzzer(LANG_GRAMMAR) 36 | graph = gg.GrammarGraph.from_grammar(LANG_GRAMMAR) 37 | 38 | for _ in range(10): 39 | inp = fuzzer.fuzz_tree() 40 | result = mutator.replace_subtree_randomly(inp) 41 | self.assertTrue(is_successful(result)) 42 | self.assertTrue(graph.tree_is_valid(result.unwrap())) 43 | 44 | def test_swap_subtrees(self): 45 | mutator = Mutator(LANG_GRAMMAR) 46 | fuzzer = GrammarCoverageFuzzer(LANG_GRAMMAR) 47 | graph = gg.GrammarGraph.from_grammar(LANG_GRAMMAR) 48 | 49 | for _ in range(10): 50 | inp = fuzzer.fuzz_tree() 51 | result = mutator.swap_subtrees(inp) 52 | 53 | self.assertTrue( 54 | is_successful(result) 55 | or len(inp.filter(lambda t: t.value == "")) == 1 56 | ) 57 | 58 | self.assertTrue( 59 | result.map(lambda tree: graph.tree_is_valid(result.unwrap())) 60 | .value_or(True) 61 | ) 62 | 63 | def test_generalize_subtree(self): 64 | mutator = Mutator(LANG_GRAMMAR) 65 | fuzzer = GrammarCoverageFuzzer(LANG_GRAMMAR) 66 | graph = gg.GrammarGraph.from_grammar(LANG_GRAMMAR) 67 | 68 | for _ in range(10): 69 | inp = fuzzer.fuzz_tree() 70 | result = mutator.generalize_subtree(inp) 71 | self.assertTrue(is_successful(result)) 72 | self.assertTrue(graph.tree_is_valid(result.unwrap())) 73 | 74 | def test_mutate(self): 75 | mutator = Mutator(LANG_GRAMMAR) 76 | fuzzer = GrammarCoverageFuzzer(LANG_GRAMMAR) 77 | graph = gg.GrammarGraph.from_grammar(LANG_GRAMMAR) 78 | 79 | for _ in range(10): 80 | inp = fuzzer.fuzz_tree() 81 | result = mutator.mutate(inp) 82 | self.assertTrue(graph.tree_is_valid(result)) 83 | 84 | 85 | def parse(inp: str, grammar: Grammar = LANG_GRAMMAR) -> DerivationTree: 86 | return DerivationTree.from_parse_tree(next(EarleyParser(grammar).parse(inp))) 87 | 88 | 89 | if __name__ == "__main__": 90 | unittest.main() 91 | -------------------------------------------------------------------------------- /tests/test_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import unittest 20 | 21 | from isla.fuzzer import GrammarCoverageFuzzer 22 | from isla.helpers import tree_to_string 23 | from isla.derivation_tree import DerivationTree 24 | from isla.parser import EarleyParser, EnhancedExtractor 25 | from test_data import LANG_GRAMMAR 26 | 27 | 28 | class TestParser(unittest.TestCase): 29 | def test_earley_parser_lang(self): 30 | parser = EarleyParser(LANG_GRAMMAR) 31 | fuzzer = GrammarCoverageFuzzer(LANG_GRAMMAR) 32 | for _ in range(100): 33 | tree = fuzzer.expand_tree(DerivationTree('')).to_parse_tree() 34 | self.assertEqual(tree, EnhancedExtractor(parser, tree_to_string(tree)).extract_a_tree()) 35 | 36 | 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /tests/test_path_indexed_trie.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import itertools 20 | import unittest 21 | 22 | from isla.derivation_tree import DerivationTree 23 | from isla.parser import EarleyParser 24 | from isla.trie import SubtreesTrie, path_to_trie_key, trie_key_to_path 25 | from test_data import LANG_GRAMMAR 26 | 27 | 28 | class TestPathIndexedTrie(unittest.TestCase): 29 | def test_trie_creation(self): 30 | parser = EarleyParser(LANG_GRAMMAR) 31 | tree = DerivationTree.from_parse_tree(next(parser.parse("x := 1 ; y := x ; y := 2 ; z := y ; x := z"))) 32 | trie = SubtreesTrie(dict(tree.paths())) 33 | 34 | self.assertEqual(trie.keys(), [path for path, _ in tree.paths()]) 35 | 36 | def test_get_keys_of_sub_trie(self): 37 | parser = EarleyParser(LANG_GRAMMAR) 38 | tree = DerivationTree.from_parse_tree(next(parser.parse("x := 1 ; y := x"))) 39 | 40 | self.assertEqual( 41 | [path for path, _ in tree.get_subtree((0, 0)).paths()], 42 | tree.trie().get_subtrie((0, 0)).keys()) 43 | 44 | def test_get_values_of_sub_trie(self): 45 | parser = EarleyParser(LANG_GRAMMAR) 46 | tree = DerivationTree.from_parse_tree(next(parser.parse("x := 1 ; y := x"))) 47 | 48 | self.assertEqual(tree.get_subtree((0, 0)).paths(), tree.trie().get_subtrie((0, 0)).values()) 49 | 50 | def test_get_items_of_sub_trie(self): 51 | parser = EarleyParser(LANG_GRAMMAR) 52 | tree = DerivationTree.from_parse_tree(next(parser.parse("x := 1 ; y := x"))) 53 | 54 | self.assertEqual( 55 | [(path, (path, st)) for path, st in tree.get_subtree((0, 0)).paths()], 56 | tree.trie().get_subtrie((0, 0)).items()) 57 | 58 | def test_get_sub_trie(self): 59 | parser = EarleyParser(LANG_GRAMMAR) 60 | tree = DerivationTree.from_parse_tree(next(parser.parse("x := 1 ; y := x ; y := 2 ; z := y ; x := z"))) 61 | 62 | for path, _ in tree.paths(): 63 | subtree_paths = [(p, t) for _, (p, t) in tree.trie().get_subtrie(path).items()] 64 | self.assertEqual([(p[len(path):], t) for p, t in tree.paths() if p[:len(path)] == path], subtree_paths) 65 | 66 | def test_get_subtrie_artificial(self): 67 | paths = [] 68 | for l in range(6): 69 | paths += list(itertools.product(*[[i for i in range(5)] for _ in range(l)])) 70 | 71 | trie = SubtreesTrie() 72 | for path in paths: 73 | trie[path] = path, None 74 | 75 | for path in paths: 76 | expected = {(p[len(path):], None) for p in paths if p[:len(path)] == path} 77 | result = {(p, t) for p, t in trie.get_subtrie(path).values()} 78 | 79 | self.assertEqual( 80 | expected, result, 81 | f"Sub-trie differs from sub-tree at path {path}" 82 | ) 83 | 84 | def test_path_to_trie_key(self): 85 | for l in range(6): 86 | paths = list(itertools.product(*[[i for i in range(5)] for _ in range(l)])) 87 | for path in paths: 88 | self.assertEqual(path, trie_key_to_path(path_to_trie_key(path))) 89 | 90 | def test_trie_key_to_path(self): 91 | self.assertEqual("\x01\x02", path_to_trie_key(trie_key_to_path("\x01\x02"))) 92 | try: 93 | trie_key_to_path("\x02") 94 | self.fail("Exception expected for trie key '\\x02'") 95 | except RuntimeError: 96 | pass 97 | 98 | 99 | if __name__ == '__main__': 100 | unittest.main() 101 | -------------------------------------------------------------------------------- /tests/test_tar_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import unittest 20 | 21 | from grammar_graph import gg 22 | 23 | from isla.helpers import tree_to_string 24 | from isla_formalizations import tar 25 | from isla_formalizations.tar import TAR_GRAMMAR 26 | 27 | 28 | class TestTarParser(unittest.TestCase): 29 | def test_parse_file_name_str(self): 30 | tar_parser = tar.TarParser() 31 | file_name_str = "bbb" 32 | parsed = tar_parser.parse_file_name_str(file_name_str) 33 | 34 | self.assertEqual(file_name_str, tree_to_string(parsed)) 35 | 36 | graph = gg.GrammarGraph.from_grammar(TAR_GRAMMAR) 37 | self.assertTrue(graph.tree_is_valid(parsed)) 38 | 39 | def test_parse_characters(self): 40 | tar_parser = tar.TarParser() 41 | file_name_str = "bbb" 42 | parsed = tar_parser.parse_characters(file_name_str) 43 | 44 | self.assertEqual(file_name_str, tree_to_string(parsed)) 45 | 46 | graph = gg.GrammarGraph.from_grammar(TAR_GRAMMAR) 47 | self.assertTrue(graph.tree_is_valid(parsed)) 48 | 49 | 50 | if __name__ == '__main__': 51 | unittest.main() 52 | -------------------------------------------------------------------------------- /tests/test_three_valued_truth.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import unittest 3 | 4 | from isla.three_valued_truth import ThreeValuedTruth 5 | 6 | 7 | class TestThreeValuedTruth(unittest.TestCase): 8 | def test_neg(self): 9 | tval = ThreeValuedTruth.unknown() 10 | try: 11 | result = not tval 12 | self.fail("AssertionError expected") 13 | except AssertionError: 14 | pass 15 | 16 | tval = ThreeValuedTruth.true() 17 | self.assertTrue((-tval).is_false()) 18 | 19 | tval = ThreeValuedTruth.false() 20 | self.assertTrue((-tval).is_true()) 21 | 22 | def test_and(self): 23 | values = [ 24 | ThreeValuedTruth.unknown(), 25 | ThreeValuedTruth.true(), 26 | ThreeValuedTruth.false(), 27 | ] 28 | 29 | for val_1, val_2 in itertools.combinations_with_replacement(values, 2): 30 | result = val_1 & val_2 31 | self.assertTrue( 32 | not val_1.is_unknown() and not val_2.is_unknown() or result.is_unknown() 33 | ) 34 | self.assertTrue( 35 | val_1.is_unknown() 36 | or val_2.is_unknown() 37 | or bool(result) == (bool(val_1) and bool(val_2)) 38 | ) 39 | 40 | def test_or(self): 41 | values = [ 42 | ThreeValuedTruth.unknown(), 43 | ThreeValuedTruth.true(), 44 | ThreeValuedTruth.false(), 45 | ] 46 | 47 | for val_1, val_2 in itertools.combinations_with_replacement(values, 2): 48 | result = val_1 | val_2 49 | self.assertTrue( 50 | not val_1.is_unknown() and not val_2.is_unknown() or result.is_unknown() 51 | ) 52 | self.assertTrue( 53 | val_1.is_unknown() 54 | or val_2.is_unknown() 55 | or bool(result) == (bool(val_1) or bool(val_2)) 56 | ) 57 | 58 | def test_str(self): 59 | self.assertEqual("TRUE", str(ThreeValuedTruth.true())) 60 | self.assertEqual("FALSE", str(ThreeValuedTruth.false())) 61 | self.assertEqual("UNKNOWN", str(ThreeValuedTruth.unknown())) 62 | 63 | 64 | if __name__ == "__main__": 65 | unittest.main() 66 | -------------------------------------------------------------------------------- /tests/xml_demo.py: -------------------------------------------------------------------------------- 1 | # Copyright © 2022 CISPA Helmholtz Center for Information Security. 2 | # Author: Dominic Steinhöfel. 3 | # 4 | # This file is part of ISLa. 5 | # 6 | # ISLa is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # ISLa is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with ISLa. If not, see . 18 | 19 | import logging 20 | import string 21 | import sys 22 | import xml.etree.ElementTree as ET 23 | from html import escape 24 | from typing import Optional, List, Dict 25 | 26 | from grammar_graph import gg 27 | 28 | from isla.fuzzer import GrammarCoverageFuzzer 29 | from isla.helpers import srange 30 | from isla.language import parse_isla 31 | from isla.derivation_tree import DerivationTree 32 | from isla.evaluator import evaluate 33 | from isla.optimizer import auto_tune_weight_vector 34 | from isla.parser import EarleyParser 35 | from isla.solver import ISLaSolver, CostSettings, GrammarBasedBlackboxCostComputer 36 | from isla_formalizations.xml_lang import XML_GRAMMAR_WITH_NAMESPACE_PREFIXES 37 | 38 | XML_GRAMMAR = { 39 | "": [""], 40 | "": [ 41 | "", 42 | "", 43 | ], 44 | "": [ 45 | "", 46 | "", 47 | "" 48 | ], 49 | "": ["<>", "< >"], 50 | "": ["</>", "< />"], 51 | "": [">"], 52 | "": ["=\"\"", " "], 53 | 54 | "": [ 55 | "", 56 | "", 57 | # "" 58 | ], 59 | # "": [ 60 | # ":", 61 | # ":"], 62 | "": srange("_" + string.ascii_letters), 63 | "": ["", ""], 64 | "": [""] + srange("-." + string.digits), 65 | "": ["", ""], 66 | "": [ 67 | escape(c, {'"': """}) 68 | for c in srange(string.ascii_letters + string.digits + "\"'. \t/?-,=:+")], 69 | } 70 | 71 | 72 | def validate_xml(inp: DerivationTree, out: Optional[List[str]] = None) -> bool: 73 | try: 74 | ET.fromstring(str(inp)) 75 | return True 76 | except Exception as err: 77 | if out is not None: 78 | out.append(str(err)) 79 | return False 80 | 81 | 82 | if __name__ == '__main__': 83 | # Demonstrate that grammar fuzzer produces "wrong" inputs 84 | fuzzer = GrammarCoverageFuzzer(XML_GRAMMAR_WITH_NAMESPACE_PREFIXES) 85 | errors: Dict[str, int] = {} 86 | for _ in range(100): 87 | inp = fuzzer.expand_tree(DerivationTree("")) 88 | out = [] 89 | if not validate_xml(inp, out): 90 | assert out 91 | error = out[0][:out[0].index(":")] 92 | errors.setdefault(error, 0) 93 | errors[error] += 1 94 | print(f"Invalid input produced by fuzzer ({out[0]}): {inp}", file=sys.stderr) 95 | 96 | print("Encountered errors: " + ", ".join({f"{e} ({n})" for e, n in errors.items()})) 97 | 98 | constraint = """ 99 | forall tree="<{ opid}[ ]> clid}>" in start: 100 | (= opid clid)""" 101 | 102 | # Check whether constraint can be parsed 103 | parsed_constraint = parse_isla(constraint, XML_GRAMMAR) 104 | 105 | # Try out evaluator 106 | c_inp = "asdf" 107 | w_inp = "asdf" 108 | 109 | c_tree = DerivationTree.from_parse_tree(next(EarleyParser(XML_GRAMMAR).parse(c_inp))) 110 | w_tree = DerivationTree.from_parse_tree(next(EarleyParser(XML_GRAMMAR).parse(w_inp))) 111 | 112 | print(evaluate(constraint, reference_tree=c_tree, grammar=XML_GRAMMAR)) 113 | print(evaluate(constraint, reference_tree=w_tree, grammar=XML_GRAMMAR)) 114 | 115 | # Try out solver 116 | 117 | # Get optimized weight vector 118 | # This can take some time (~3 min). 119 | # When re-running the solver, the returned cost vector should be added literally. 120 | logging.basicConfig(level=logging.INFO) 121 | tune_result = auto_tune_weight_vector( 122 | XML_GRAMMAR, 123 | parsed_constraint, 124 | validator=validate_xml, 125 | timeout=30, # How long should a single configuration be evaluated 126 | population_size=20, # How many configurations should be produced at the beginning 127 | generations=4, # Evolutionary tuning: How many generations should I produce using crossover / mutation 128 | cpu_count=-1 # Run in parallel: Use all cores (cpu_count == 1 implies single-threaded) 129 | ) 130 | 131 | print(f"Optimal cost vector: {tune_result[1]}") 132 | 133 | # Result is something like 134 | # CostWeightVector( 135 | # tree_closing_cost=15, 136 | # constraint_cost=0, 137 | # derivation_depth_penalty=0, 138 | # low_k_coverage_penalty=5, 139 | # low_global_k_path_coverage_penalty=7) 140 | 141 | # Run solver 142 | solver = ISLaSolver( 143 | XML_GRAMMAR, 144 | constraint, 145 | max_number_smt_instantiations=1, # Number of solutions for symbols bound by SMT formulas 146 | max_number_free_instantiations=1, # Number of solutions for symbols not bound by any formula 147 | cost_computer=GrammarBasedBlackboxCostComputer(CostSettings( 148 | tune_result[1], # Cost weight 149 | k=3 # For k-Path coverage: Sequences of which length in grammar graph / trees should be considered 150 | ), gg.GrammarGraph.from_grammar(XML_GRAMMAR)), 151 | ) 152 | 153 | for _ in range(100): 154 | inp = solver.solve() 155 | print(inp) 156 | if not validate_xml(inp): 157 | print(f"Invalid input produced by ISLa solver: {inp}", file=sys.stderr) 158 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py310 3 | 4 | [testenv] 5 | # install pytest in the virtualenv where commands will be executed 6 | deps = 7 | pytest 8 | -rrequirements_test.txt 9 | commands = 10 | # NOTE: you can run any command line tool here - not just tests 11 | pytest -n 16 --html=report.html --self-contained-html --cov=isla tests --cov-config=.coveragerc 12 | --------------------------------------------------------------------------------