├── .codecov.yml ├── .coveragerc ├── .flake8 ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── release-drafter.yml └── workflows │ └── main.yaml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── conftest.py ├── fixtures ├── file └── test │ └── file ├── images ├── logo.png ├── main_demo.gif ├── pcap_demo.gif ├── subcategories.png └── wannacry_demo.png ├── noxfile.py ├── poetry.lock ├── pyproject.toml ├── pywhat ├── Data │ ├── file_signatures.json │ ├── mac_vendors.json │ ├── mastercard_companies.json │ ├── phone_codes.json │ └── regex.json ├── __init__.py ├── __main__.py ├── filter.py ├── helper.py ├── identifier.py ├── magic_numbers.py ├── printer.py ├── regex_identifier.py └── what.py ├── scripts ├── format_regex.py ├── get_file_sigs.py └── get_tlds.py └── tests ├── __init__.py ├── test_click.py ├── test_filtration.py ├── test_identifier.py ├── test_regex_database.py └── test_regex_identifier.py /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: 90% 6 | patch: 7 | default: 8 | target: 80% 9 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | ./noxfile.py 4 | [report] 5 | exclude_lines = 6 | pragma: no cover 7 | if __name__ == .__main__.: 8 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | ignore = E501, E203, W503 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: bee-san 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: hacker 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://www.buymeacoffee.com/beecodes", "http://paypal.me/brandonskerritt"] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 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 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 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/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **⚠ Pull Requests not made with this template will be automatically closed 🔥** 2 | 3 | ## Prerequisites 4 | - [ ] Have you read the documentation on contributing? https://github.com/bee-san/pyWhat/wiki/Adding-your-own-Regex 5 | 6 | ## Why do we need this pull request? 7 | * Explain the _why_ behind your PR. We can see what it does from the code. But _why_ does it do that? 8 | 9 | ## What [GitHub issues](https://github.com/bee-san/pyWhat/issues) does this fix? 10 | * Fixes #10000 11 | 12 | ## Copy / paste of output 13 | 14 | Please copy and paste the output of PyWhat with your new addition using an example that tests this addition below: 15 | 16 | ```console 17 | 18 | ``` 19 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | template: | 2 | ## What’s Changed 3 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | # .github/workflows/tests.yml 2 | name: Tests 3 | on: [push, pull_request] 4 | jobs: 5 | tests: 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | python-version: ['3.7', '3.8', '3.9', '3.10'] 10 | os: [ubuntu-latest, windows-latest, macos-latest] 11 | runs-on: ${{ matrix.os }} 12 | name: Python ${{ matrix.python-version }} on ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-python@v1 16 | with: 17 | python-version: ${{ matrix.python-version }} 18 | architecture: x64 19 | - run: python -m pip install nox poetry 20 | - run: python -m nox 21 | env: 22 | OS: ${{ matrix.os }} 23 | PYTHON: ${{ matrix.python-version }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # Created by https://www.toptal.com/developers/gitignore/api/python 18 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 19 | 20 | ### Python ### 21 | # Byte-compiled / optimized / DLL files 22 | __pycache__/ 23 | *.py[cod] 24 | *$py.class 25 | tests/__pycache__/ 26 | 27 | # C extensions 28 | *.so 29 | 30 | # Distribution / packaging 31 | .Python 32 | build/ 33 | develop-eggs/ 34 | dist/ 35 | downloads/ 36 | eggs/ 37 | .eggs/ 38 | parts/ 39 | sdist/ 40 | var/ 41 | wheels/ 42 | pip-wheel-metadata/ 43 | share/python-wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # PyInstaller 50 | # Usually these files are written by a python script from a template 51 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 52 | *.manifest 53 | *.spec 54 | 55 | # Installer logs 56 | pip-log.txt 57 | pip-delete-this-directory.txt 58 | 59 | # Unit test / coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .nox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | *.py,cover 70 | .hypothesis/ 71 | .pytest_cache/ 72 | pytestdebug.log 73 | 74 | # Translations 75 | *.mo 76 | *.pot 77 | 78 | # Django stuff: 79 | *.log 80 | local_settings.py 81 | db.sqlite3 82 | db.sqlite3-journal 83 | 84 | # Flask stuff: 85 | instance/ 86 | .webassets-cache 87 | 88 | # Scrapy stuff: 89 | .scrapy 90 | 91 | # Sphinx documentation 92 | docs/_build/ 93 | doc/_build/ 94 | 95 | # PyBuilder 96 | target/ 97 | 98 | # Jupyter Notebook 99 | .ipynb_checkpoints 100 | 101 | # IPython 102 | profile_default/ 103 | ipython_config.py 104 | 105 | # pyenv 106 | .python-version 107 | 108 | # pipenv 109 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 110 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 111 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 112 | # install all needed dependencies. 113 | #Pipfile.lock 114 | 115 | # poetry 116 | #poetry.lock 117 | 118 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 119 | __pypackages__/ 120 | 121 | # Celery stuff 122 | celerybeat-schedule 123 | celerybeat.pid 124 | 125 | # SageMath parsed files 126 | *.sage.py 127 | 128 | # Environments 129 | # .env 130 | .env/ 131 | .venv/ 132 | env/ 133 | venv/ 134 | ENV/ 135 | env.bak/ 136 | venv.bak/ 137 | pythonenv* 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # operating system-related files 161 | # file properties cache/storage on macOS 162 | *.DS_Store 163 | # thumbnail cache on Windows 164 | Thumbs.db 165 | 166 | # profiling data 167 | .prof 168 | 169 | 170 | # End of https://www.toptal.com/developers/gitignore/api/python 171 | 172 | # Code editor files 173 | .vscode 174 | .idea -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Please read our wiki here: 2 | https://github.com/bee-san/pyWhat/wiki/Adding-your-own-Regex 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-alpine 2 | RUN apk add --no-cache git \ 3 | && pip install --no-cache-dir git+https://github.com/bee-san/pyWhat \ 4 | && apk del git \ 5 | && rm -rf /var/cache/apk/* 6 | WORKDIR /workdir 7 | ENTRYPOINT [ "python", "-m", "pywhat" ] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 - Present @bee-san 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

➡️ Discord ⬅️
4 | The easiest way to identify anything
5 | pip3 install pywhat && pywhat --help 6 |

7 | 8 |

9 | Discord PyPI - Downloads Twitter Follow PyPI - Python Version PyPI 10 |

11 |
12 | 13 | # 🤔 `What` is this? 14 | 15 | ![](images/main_demo.gif) 16 | 17 | Imagine this: You come across some mysterious text 🧙‍♂️ `0x52908400098527886E0F7030069857D2E4169EE7` or `dQw4w9WgXcQ` and you wonder what it is. What do you do? 18 | 19 | Well, with `what` all you have to do is ask `what "0x52908400098527886E0F7030069857D2E4169EE7"` and `what` will tell you! 20 | 21 | `what`'s job is to **identify _what_ something is.** Whether it be a file or text! Or even the hex of a file! What about text _within_ files? We have that too! `what` is recursive, it will identify **everything** in text and more! 22 | 23 | # Installation 24 | 25 | ## 🔨 Using pip 26 | 27 | ```$ pip3 install pywhat``` 28 | 29 | or 30 | 31 | ```shell 32 | # installs optional dependencies that may improve the speed 33 | $ pip3 install pywhat[optimize] 34 | ``` 35 | 36 | ## 🔨 On Mac? 37 | 38 | ```$ brew install pywhat``` 39 | 40 | Or for our MacPorts fans: 41 | 42 | ```$ sudo port install pywhat``` 43 | 44 | # ⚙ Use Cases 45 | 46 | ## 🦠 Wannacry 47 | 48 | ![](images/wannacry_demo.png) 49 | 50 | You come across a new piece of malware called WantToCry. You think back to Wannacry and remember it was stopped because a researcher found a kill-switch in the code. 51 | 52 | When a domain, hardcoded into Wannacry, was registered the virus would stop. 53 | 54 | You use `What` to identify all the domains in the malware, and use a domain registrar API to register all the domains. 55 | 56 | ## 🦈 Faster Analysis of Pcap files 57 | 58 | ![](images/pcap_demo.gif) 59 | 60 | Say you have a `.pcap` file from a network attack. `What` can identify this and quickly find you: 61 | 62 | - All URLs 63 | - Emails 64 | - Phone numbers 65 | - Credit card numbers 66 | - Cryptocurrency addresses 67 | - Social Security Numbers 68 | - and much more. 69 | 70 | With `what`, you can identify the important things in the pcap in seconds, not minutes. 71 | 72 | ## 🐞 Bug Bounties 73 | 74 | You can use PyWhat to scan for things that'll make you money via bug bounties like: 75 | * API Keys 76 | * Webhooks 77 | * Credentials 78 | * and more 79 | 80 | Run PyWhat with: 81 | 82 | ``` 83 | pywhat --include "Bug Bounty" TEXT 84 | ``` 85 | 86 | To do this. 87 | 88 | Here are some examples 👇 89 | 90 | ### 🐙 GitHub Repository API Key Leaks 91 | 92 | 1. Download all GitHub repositories of an organisation 93 | 2. Search for anything that you can submit as a bounty, like API keys 94 | 95 | ```shell 96 | # Download all repositories 97 | GHUSER=CHANGEME; curl "https://api.github.com/users/$GHUSER/repos?per_page=1000" | grep -o 'git@[^"]*' | xargs -L1 git clone 98 | 99 | # Will print when it finds things. 100 | # Loops over all files in current directory. 101 | find . -type f -execdir pywhat --include 'Bug Bounty' {} \; 102 | ``` 103 | 104 | ### 🕷 Scan all web pages for bounties 105 | 106 | ```shell 107 | # Recursively download all web pages of a site 108 | wget -r -np -k https://skerritt.blog 109 | 110 | # Will print when it finds things. 111 | # Loops over all files in current directory. 112 | find . -type f -execdir pywhat --include 'Bug Bounty' {} \; 113 | ``` 114 | 115 | **PS**: We support more filters than just bug bounties! Run `pywhat --tags` 116 | 117 | ## 🌌 Other Features 118 | 119 | Anytime you have a file and you want to find structured data in it that's useful, `What` is for you. 120 | 121 | Or if you come across some piece of text and you don't know what it is, `What` will tell you. 122 | 123 | ### 📁 File & Directory Handling 124 | 125 | **File Opening** You can pass in a file path by `what 'this/is/a/file/path'`. `What` is smart enough to figure out it's a file! 126 | 127 | What about a whole **directory**? `What` can handle that too! It will **recursively** search for files and output everything you need! 128 | 129 | ### 🔍 Filtering your output 130 | 131 | Sometimes, you only care about seeing things which are related to AWS. Or bug bounties, or cryptocurrencies! 132 | 133 | You can filter output by using `what --rarity 0.2:0.8 --include Identifiers,URL https://skerritt.blog`. Use `what --help` to get more information. 134 | 135 | To see all filters, run `pywhat --tags`! You can also combine them, for example to see all cryptocurrency wallets minus Ripple you can do: 136 | 137 | ```console 138 | pywhat --include "Cryptocurrency Wallet" --exclude "Ripple Wallet" 1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY 139 | ``` 140 | 141 | ### 👽 Sorting, Exporting, and more! 142 | 143 | **Sorting** You can sort the output by using `what -k rarity --reverse TEXT`. Use `what --help` to get more information. 144 | 145 | **Exporting** You can export to json using `what --json` and results can be sent directly to a file using `what --json > file.json`. 146 | 147 | **Boundaryless mode** `What` has a special mode to match identifiable information within strings. By default, it is enabled in CLI but disabled in API. Use `what --help` or refer to [API Documentation](https://github.com/bee-san/pyWhat/wiki/API) for more information. 148 | 149 | 150 | # 🍕 API 151 | 152 | PyWhat has an API! Click here [https://github.com/bee-san/pyWhat/wiki/API](https://github.com/bee-san/pyWhat/wiki/API) to read about it. 153 | 154 | # 👾 Contributing 155 | 156 | `what` not only thrives on contributors, but can't exist without them! If you want to add a new regex to check for things, you can read our documentation [here](https://github.com/bee-san/what/wiki/Adding-your-own-Regex) 157 | 158 | We ask contributors to join the Discord for quicker discussions, but it's not needed: 159 | Discord 160 | 161 | # 🙏 Thanks 162 | 163 | We would like to thank [Dora](https://github.com/sdushantha/dora) for their work on a bug bounty specific regex database which we have used. -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | def pytest_configure(config): 5 | # Silence Flake8 warnings 6 | logging.getLogger("flake8").setLevel(logging.ERROR) 7 | -------------------------------------------------------------------------------- /fixtures/file: -------------------------------------------------------------------------------- 1 | 0x52908400098527886E0F7030069857D2E4169EE7 2 | DANHz6EQVoWyZ9rER56DwTXHWUxfkv9k2o 3 | print("hello) 4 | 5 | thm{"Can you guess what this is, now?"} 6 | THM{this is a flag} 7 | 0x52908400098527886E0F7030069857D2E4169EE730000000000004 8 | 0x52908400098527886E0F7030069857D2E4169EE7 9 | @pytest.mark.skip(reason="Fails Regex due to http://") 10 | "1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY" 11 | 16ftSEQ4ctQFDtVZiUBusQUjRrGhM3JYwe 12 | 4462030000000000 13 | thm{"uh hello?"} 14 | 001-01-0001 15 | flag{"helo jenny dwi'n gwybod eich bod chi'n darllen hwn diolch am fod yn ffrind i mi "} 16 | 340000000000009 17 | 30569309025904 18 | http://10.1.1.1 19 | https://www.youtube.com/watch?v=ScOAntcCa78 20 | adsadasdasdhttps://www.youtube.com/watch?v=trj15fjXWDwasdasdasd 21 | 6011000000000004 22 | htb{4111111111111111} 23 | 3000 0000 0000 04 24 | 5500000000000004 25 | 26 | james:S3cr37_P@$$W0rd 27 | ScOAntcCa78 28 |

hello

29 | 127.0.0.1 30 | github@skerritt.blog 31 | 32 | Access-Control-Allow-Headers: * 33 | 34 | 47DF8D9NwtmefhFUghynYRMqrexiZTsm48T1hhi2jZcbfcwoPbkhMrrED6zqJRfeYpXFfdaqAT3jnBEwoMwCx6BYDJ1W3ub 35 | LRX8rSPVjifTxoLeoJtLf2JYdJFTQFcE7m 36 | bitcoincash:qzlg6uvceehgzgtz6phmvy8gtdqyt6vf359at4n3lq 37 | rBPAQmwMrt7FDDPNyjwFgwSqbWZPf6SLkk 38 | 2001:0db8:85a3:0000:0000:8a2e:0370:7334 39 | de:ad:be:ef:ca:fe 40 | DE:AD:BE:EF:CA:FE 41 | 42 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 43 | fajfdk;fa+91 (385) 985 2821kl;fajf;la 44 | 45 | ssh-rsa AAAAB3NzaC1tc2EAAAADAQABAAACAQDrnjkGtf3iA46rtwsvRiscvMTCw30l5Mmln/sf8Wohg4RPc7nuIx3/fB86K9jzBNoQk6Fb00p2cSW0dX6c3OTL5R5Q0rBjOFy6GV07MkS7rXa7WYh4ObxBh+M+LEzxVIw29anzQFZkR0TAf6x2rBoErK7JYU4fyqFBDFupTt3coQDPEEmVwtLLUCEnJrurbbnJKcWJ+/FbItLxYyMLPl8TwEn0iqiJ97onCU2DuBtiYv3hp1WoEH08b5WDF0F04zEPRdJT+WisxlEFRgaj51o2BtjOC+D2qZQDb4LHaAfJ0WcO4nu7YocdlcLp2JPfXKKgu9P5J8UDsmXbR3KCJ1oddFa2R6TbHc6d2hKyG4amBzMX5ltxXu7D6FLPZlFqua8YooY7A2zwIVirOUH/cfx+5O9o0CtspkNmj/iYzN0FPaOpELncWsuauy9hrGql/1cF4SUr20zHFoBoDQUtmvmBnWnKoGfpWXzuda449FVtmcrEjvBzCvCb3RStu0BbyOOybJagbKif3MkcYVO10pRbTveIUwgCD6F3ypD11XztoPNsgScmjme0sj/KWWNLyQkLWtpJEQ4k46745NAC5g+nP28TR2JM8doeqsxA8JovQkLWwDcR+WYZu2z/I8dfhOmalnoMRTJ2NzWDc0OSkKGYWjexR4fN6lAKCUOUptl9Nw== r00t@my-random_host 46 | ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= r00t@my-random_host 47 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0wmN/Cr3JXqmLW7u+g9pTh+wyqDHpSQEIQczXkVx9q r00t@my-random_host 48 | 49 | -----BEGIN PGP PUBLIC KEY BLOCK----- 50 | Comment: Alice's OpenPGP certificate 51 | Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html 52 | 53 | mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U 54 | b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE 55 | ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy 56 | MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO 57 | dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 58 | OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s 59 | E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb 60 | DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn 61 | 0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= 62 | =iIGO 63 | -----END PGP PUBLIC KEY BLOCK----- 64 | 65 | -----BEGIN PGP PRIVATE KEY BLOCK----- 66 | Comment: Alice's OpenPGP Transferable Secret Key 67 | Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html 68 | 69 | lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U 70 | b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj 71 | ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ 72 | CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l 73 | nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf 74 | a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB 75 | BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA 76 | /3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF 77 | u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM 78 | hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb 79 | Pnn+We1aTBhaGa86AQ== 80 | =n8OM 81 | -----END PGP PRIVATE KEY BLOCK----- 82 | ======= 83 | B07ND5BB8V 84 | 85 | 34A2344 86 | 13-08-1987 87 | 12345678902 88 | 1234567890 89 | 90 | nano_1c46rz7xnk98ozhzdjq7thwty844sgnqxk9496yysit1bnio1rcdzshc5ymn 91 | 92 | b2ced6f5-2542-4f7d-b131-e3ada95d8b75 93 | 5fc7c33a7ef88b139122a38a 94 | 01ERJ58HMWDN3VTRRHZQV2T5R5 95 | 96 | otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example 97 | 98 | sshpass -p MyPassw0RD! ssh root@10.0.0.10 99 | 100 | https://hooks.slack.com/services/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 101 | 102 | doi:10.1392/BC1.0 103 | 10.1000/123 104 | 105 | a80122b2565c3e26a61cbf58d1d1aad7-us5 106 | 107 | https://discord.com/api/webhooks/949053171232276500/V4xxtMKjN9_xH_L6ipXkNRjUqZSAiYQ2l64-L0Z4-3ciPc6tsDh25zLaEEjp97U5wXN2 108 | 109 | https://www.guilded.gg/api/webhooks/1fa5fe35-74e7-4f17-bf53-52d02293fea6/zhXzVecRvaMCWEcOCIuIMYQyeeKemUCswca24Q68cCQCgg4oYKeawamAiEkaI2uCS8Q2sgwy2qUOe2c4yE2em6 110 | -------------------------------------------------------------------------------- /fixtures/test/file: -------------------------------------------------------------------------------- 1 | https://google.com -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bee-san/pyWhat/75a1592cbc4151acdaaeab78b71dd65ea9a9230e/images/logo.png -------------------------------------------------------------------------------- /images/main_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bee-san/pyWhat/75a1592cbc4151acdaaeab78b71dd65ea9a9230e/images/main_demo.gif -------------------------------------------------------------------------------- /images/pcap_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bee-san/pyWhat/75a1592cbc4151acdaaeab78b71dd65ea9a9230e/images/pcap_demo.gif -------------------------------------------------------------------------------- /images/subcategories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bee-san/pyWhat/75a1592cbc4151acdaaeab78b71dd65ea9a9230e/images/subcategories.png -------------------------------------------------------------------------------- /images/wannacry_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bee-san/pyWhat/75a1592cbc4151acdaaeab78b71dd65ea9a9230e/images/wannacry_demo.png -------------------------------------------------------------------------------- /noxfile.py: -------------------------------------------------------------------------------- 1 | """Nox sessions.""" 2 | from os import environ 3 | from typing import Any 4 | 5 | import nox 6 | from nox.sessions import Session 7 | 8 | nox.options.sessions = ["tests"] 9 | if environ.get("CI", None) == "true": 10 | nox.options.sessions.append("coverage") 11 | locations = "src", "tests", "noxfile.py", "docs/conf.py" 12 | 13 | 14 | def install_with_constraints(session: Session, *args: str, **kwargs: Any) -> None: 15 | """Install packages constrained by Poetry's lock file. 16 | This function is a wrapper for nox.sessions.Session.install. It 17 | invokes pip to install packages inside of the session's virtualenv. 18 | Additionally, pip is passed a constraints file generated from 19 | Poetry's lock file, to ensure that the packages are pinned to the 20 | versions specified in poetry.lock. This allows you to manage the 21 | packages as Poetry development dependencies. 22 | Arguments: 23 | session: The Session object. 24 | args: Command-line arguments for pip. 25 | kwargs: Additional keyword arguments for Session.install. 26 | """ 27 | session.run( 28 | "poetry", 29 | "export", 30 | "--without-hashes", 31 | "--dev", 32 | "--format=requirements.txt", 33 | "--output=requirements.txt", 34 | external=True, 35 | ) 36 | session.install("--constraint=requirements.txt", *args, **kwargs) 37 | 38 | 39 | @nox.session 40 | def tests(session: Session) -> None: 41 | """Run the test suite.""" 42 | session.run("poetry", "install", "--no-dev", external=True) 43 | install_with_constraints( 44 | session, 45 | "pytest", 46 | "pytest-black", 47 | "pytest-cov", 48 | "pytest-isort", 49 | "pytest-flake8", 50 | "pytest-mypy", 51 | "types-requests", 52 | "types-orjson", 53 | ) 54 | session.run("pytest", "--cov=./", "--cov-report=xml") 55 | 56 | 57 | @nox.session 58 | def coverage(session: Session) -> None: 59 | """Upload coverage data.""" 60 | install_with_constraints(session, "codecov") 61 | session.run("codecov", "--env", "OS", "PYTHON") 62 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "atomicwrites" 3 | version = "1.4.0" 4 | description = "Atomic file writes." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 8 | 9 | [[package]] 10 | name = "attrs" 11 | version = "21.4.0" 12 | description = "Classes Without Boilerplate" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 16 | 17 | [package.extras] 18 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 19 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 21 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 22 | 23 | [[package]] 24 | name = "black" 25 | version = "22.3.0" 26 | description = "The uncompromising code formatter." 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=3.6.2" 30 | 31 | [package.dependencies] 32 | click = ">=8.0.0" 33 | mypy-extensions = ">=0.4.3" 34 | pathspec = ">=0.9.0" 35 | platformdirs = ">=2" 36 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 37 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} 38 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 39 | 40 | [package.extras] 41 | colorama = ["colorama (>=0.4.3)"] 42 | d = ["aiohttp (>=3.7.4)"] 43 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 44 | uvloop = ["uvloop (>=0.15.2)"] 45 | 46 | [[package]] 47 | name = "certifi" 48 | version = "2021.10.8" 49 | description = "Python package for providing Mozilla's CA Bundle." 50 | category = "dev" 51 | optional = false 52 | python-versions = "*" 53 | 54 | [[package]] 55 | name = "charset-normalizer" 56 | version = "2.0.12" 57 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 58 | category = "dev" 59 | optional = false 60 | python-versions = ">=3.5.0" 61 | 62 | [package.extras] 63 | unicode_backport = ["unicodedata2"] 64 | 65 | [[package]] 66 | name = "click" 67 | version = "8.1.2" 68 | description = "Composable command line interface toolkit" 69 | category = "main" 70 | optional = false 71 | python-versions = ">=3.7" 72 | 73 | [package.dependencies] 74 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 75 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 76 | 77 | [[package]] 78 | name = "colorama" 79 | version = "0.4.4" 80 | description = "Cross-platform colored terminal text." 81 | category = "main" 82 | optional = false 83 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 84 | 85 | [[package]] 86 | name = "commonmark" 87 | version = "0.9.1" 88 | description = "Python parser for the CommonMark Markdown spec" 89 | category = "main" 90 | optional = false 91 | python-versions = "*" 92 | 93 | [package.extras] 94 | test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] 95 | 96 | [[package]] 97 | name = "filelock" 98 | version = "3.6.0" 99 | description = "A platform independent file lock." 100 | category = "dev" 101 | optional = false 102 | python-versions = ">=3.7" 103 | 104 | [package.extras] 105 | docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] 106 | testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] 107 | 108 | [[package]] 109 | name = "flake8" 110 | version = "4.0.1" 111 | description = "the modular source code checker: pep8 pyflakes and co" 112 | category = "dev" 113 | optional = false 114 | python-versions = ">=3.6" 115 | 116 | [package.dependencies] 117 | importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} 118 | mccabe = ">=0.6.0,<0.7.0" 119 | pycodestyle = ">=2.8.0,<2.9.0" 120 | pyflakes = ">=2.4.0,<2.5.0" 121 | 122 | [[package]] 123 | name = "idna" 124 | version = "3.3" 125 | description = "Internationalized Domain Names in Applications (IDNA)" 126 | category = "dev" 127 | optional = false 128 | python-versions = ">=3.5" 129 | 130 | [[package]] 131 | name = "importlib-metadata" 132 | version = "4.2.0" 133 | description = "Read metadata from Python packages" 134 | category = "main" 135 | optional = false 136 | python-versions = ">=3.6" 137 | 138 | [package.dependencies] 139 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 140 | zipp = ">=0.5" 141 | 142 | [package.extras] 143 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 144 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] 145 | 146 | [[package]] 147 | name = "iniconfig" 148 | version = "1.1.1" 149 | description = "iniconfig: brain-dead simple config-ini parsing" 150 | category = "dev" 151 | optional = false 152 | python-versions = "*" 153 | 154 | [[package]] 155 | name = "isort" 156 | version = "5.10.1" 157 | description = "A Python utility / library to sort Python imports." 158 | category = "dev" 159 | optional = false 160 | python-versions = ">=3.6.1,<4.0" 161 | 162 | [package.extras] 163 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 164 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 165 | colors = ["colorama (>=0.4.3,<0.5.0)"] 166 | plugins = ["setuptools"] 167 | 168 | [[package]] 169 | name = "mccabe" 170 | version = "0.6.1" 171 | description = "McCabe checker, plugin for flake8" 172 | category = "dev" 173 | optional = false 174 | python-versions = "*" 175 | 176 | [[package]] 177 | name = "mypy" 178 | version = "0.942" 179 | description = "Optional static typing for Python" 180 | category = "dev" 181 | optional = false 182 | python-versions = ">=3.6" 183 | 184 | [package.dependencies] 185 | mypy-extensions = ">=0.4.3" 186 | tomli = ">=1.1.0" 187 | typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} 188 | typing-extensions = ">=3.10" 189 | 190 | [package.extras] 191 | dmypy = ["psutil (>=4.0)"] 192 | python2 = ["typed-ast (>=1.4.0,<2)"] 193 | reports = ["lxml"] 194 | 195 | [[package]] 196 | name = "mypy-extensions" 197 | version = "0.4.3" 198 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 199 | category = "dev" 200 | optional = false 201 | python-versions = "*" 202 | 203 | [[package]] 204 | name = "orjson" 205 | version = "3.6.8" 206 | description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" 207 | category = "main" 208 | optional = true 209 | python-versions = ">=3.7" 210 | 211 | [[package]] 212 | name = "packaging" 213 | version = "21.3" 214 | description = "Core utilities for Python packages" 215 | category = "dev" 216 | optional = false 217 | python-versions = ">=3.6" 218 | 219 | [package.dependencies] 220 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 221 | 222 | [[package]] 223 | name = "pathspec" 224 | version = "0.9.0" 225 | description = "Utility library for gitignore style pattern matching of file paths." 226 | category = "dev" 227 | optional = false 228 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 229 | 230 | [[package]] 231 | name = "platformdirs" 232 | version = "2.5.2" 233 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 234 | category = "dev" 235 | optional = false 236 | python-versions = ">=3.7" 237 | 238 | [package.extras] 239 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] 240 | test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] 241 | 242 | [[package]] 243 | name = "pluggy" 244 | version = "1.0.0" 245 | description = "plugin and hook calling mechanisms for python" 246 | category = "dev" 247 | optional = false 248 | python-versions = ">=3.6" 249 | 250 | [package.dependencies] 251 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 252 | 253 | [package.extras] 254 | dev = ["pre-commit", "tox"] 255 | testing = ["pytest", "pytest-benchmark"] 256 | 257 | [[package]] 258 | name = "py" 259 | version = "1.11.0" 260 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 261 | category = "dev" 262 | optional = false 263 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 264 | 265 | [[package]] 266 | name = "pycodestyle" 267 | version = "2.8.0" 268 | description = "Python style guide checker" 269 | category = "dev" 270 | optional = false 271 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 272 | 273 | [[package]] 274 | name = "pyflakes" 275 | version = "2.4.0" 276 | description = "passive checker of Python programs" 277 | category = "dev" 278 | optional = false 279 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 280 | 281 | [[package]] 282 | name = "pygments" 283 | version = "2.12.0" 284 | description = "Pygments is a syntax highlighting package written in Python." 285 | category = "main" 286 | optional = false 287 | python-versions = ">=3.6" 288 | 289 | [[package]] 290 | name = "pyparsing" 291 | version = "3.0.8" 292 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 293 | category = "dev" 294 | optional = false 295 | python-versions = ">=3.6.8" 296 | 297 | [package.extras] 298 | diagrams = ["railroad-diagrams", "jinja2"] 299 | 300 | [[package]] 301 | name = "pytest" 302 | version = "7.1.2" 303 | description = "pytest: simple powerful testing with Python" 304 | category = "dev" 305 | optional = false 306 | python-versions = ">=3.7" 307 | 308 | [package.dependencies] 309 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 310 | attrs = ">=19.2.0" 311 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 312 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 313 | iniconfig = "*" 314 | packaging = "*" 315 | pluggy = ">=0.12,<2.0" 316 | py = ">=1.8.2" 317 | tomli = ">=1.0.0" 318 | 319 | [package.extras] 320 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 321 | 322 | [[package]] 323 | name = "pytest-black" 324 | version = "0.3.12" 325 | description = "A pytest plugin to enable format checking with black" 326 | category = "dev" 327 | optional = false 328 | python-versions = ">=2.7" 329 | 330 | [package.dependencies] 331 | black = {version = "*", markers = "python_version >= \"3.6\""} 332 | pytest = ">=3.5.0" 333 | toml = "*" 334 | 335 | [[package]] 336 | name = "pytest-flake8" 337 | version = "1.1.1" 338 | description = "pytest plugin to check FLAKE8 requirements" 339 | category = "dev" 340 | optional = false 341 | python-versions = "*" 342 | 343 | [package.dependencies] 344 | flake8 = ">=4.0" 345 | pytest = ">=7.0" 346 | 347 | [[package]] 348 | name = "pytest-isort" 349 | version = "2.0.0" 350 | description = "py.test plugin to check import ordering using isort" 351 | category = "dev" 352 | optional = false 353 | python-versions = "*" 354 | 355 | [package.dependencies] 356 | isort = ">=4.0" 357 | 358 | [package.extras] 359 | tests = ["mock"] 360 | 361 | [[package]] 362 | name = "pytest-mypy" 363 | version = "0.8.1" 364 | description = "Mypy static type checker plugin for Pytest" 365 | category = "dev" 366 | optional = false 367 | python-versions = ">=3.5" 368 | 369 | [package.dependencies] 370 | attrs = ">=19.0" 371 | filelock = ">=3.0" 372 | mypy = [ 373 | {version = ">=0.500", markers = "python_version < \"3.8\""}, 374 | {version = ">=0.700", markers = "python_version >= \"3.8\" and python_version < \"3.9\""}, 375 | {version = ">=0.780", markers = "python_version >= \"3.9\""}, 376 | ] 377 | pytest = ">=3.5" 378 | 379 | [[package]] 380 | name = "requests" 381 | version = "2.27.1" 382 | description = "Python HTTP for Humans." 383 | category = "dev" 384 | optional = false 385 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 386 | 387 | [package.dependencies] 388 | certifi = ">=2017.4.17" 389 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 390 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 391 | urllib3 = ">=1.21.1,<1.27" 392 | 393 | [package.extras] 394 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 395 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 396 | 397 | [[package]] 398 | name = "rich" 399 | version = "12.2.0" 400 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 401 | category = "main" 402 | optional = false 403 | python-versions = ">=3.6.3,<4.0.0" 404 | 405 | [package.dependencies] 406 | commonmark = ">=0.9.0,<0.10.0" 407 | pygments = ">=2.6.0,<3.0.0" 408 | typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} 409 | 410 | [package.extras] 411 | jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] 412 | 413 | [[package]] 414 | name = "toml" 415 | version = "0.10.2" 416 | description = "Python Library for Tom's Obvious, Minimal Language" 417 | category = "dev" 418 | optional = false 419 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 420 | 421 | [[package]] 422 | name = "tomli" 423 | version = "2.0.1" 424 | description = "A lil' TOML parser" 425 | category = "dev" 426 | optional = false 427 | python-versions = ">=3.7" 428 | 429 | [[package]] 430 | name = "typed-ast" 431 | version = "1.5.3" 432 | description = "a fork of Python 2 and 3 ast modules with type comment support" 433 | category = "dev" 434 | optional = false 435 | python-versions = ">=3.6" 436 | 437 | [[package]] 438 | name = "types-orjson" 439 | version = "3.6.2" 440 | description = "Typing stubs for orjson" 441 | category = "dev" 442 | optional = false 443 | python-versions = "*" 444 | 445 | [[package]] 446 | name = "types-requests" 447 | version = "2.27.20" 448 | description = "Typing stubs for requests" 449 | category = "dev" 450 | optional = false 451 | python-versions = "*" 452 | 453 | [package.dependencies] 454 | types-urllib3 = "<1.27" 455 | 456 | [[package]] 457 | name = "types-urllib3" 458 | version = "1.26.13" 459 | description = "Typing stubs for urllib3" 460 | category = "dev" 461 | optional = false 462 | python-versions = "*" 463 | 464 | [[package]] 465 | name = "typing-extensions" 466 | version = "4.2.0" 467 | description = "Backported and Experimental Type Hints for Python 3.7+" 468 | category = "main" 469 | optional = false 470 | python-versions = ">=3.7" 471 | 472 | [[package]] 473 | name = "urllib3" 474 | version = "1.26.9" 475 | description = "HTTP library with thread-safe connection pooling, file post, and more." 476 | category = "dev" 477 | optional = false 478 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 479 | 480 | [package.extras] 481 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] 482 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 483 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 484 | 485 | [[package]] 486 | name = "zipp" 487 | version = "3.8.0" 488 | description = "Backport of pathlib-compatible object wrapper for zip files" 489 | category = "main" 490 | optional = false 491 | python-versions = ">=3.7" 492 | 493 | [package.extras] 494 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] 495 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] 496 | 497 | [extras] 498 | optimize = ["orjson"] 499 | 500 | [metadata] 501 | lock-version = "1.1" 502 | python-versions = "^3.7" 503 | content-hash = "c7df16c9b4c5c23eb5259af6d9d30561ebd28eb8f201a748f73580d1f3811245" 504 | 505 | [metadata.files] 506 | atomicwrites = [ 507 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 508 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 509 | ] 510 | attrs = [ 511 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 512 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 513 | ] 514 | black = [ 515 | {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, 516 | {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, 517 | {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, 518 | {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, 519 | {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, 520 | {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, 521 | {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, 522 | {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, 523 | {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, 524 | {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, 525 | {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, 526 | {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, 527 | {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, 528 | {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, 529 | {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, 530 | {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, 531 | {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, 532 | {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, 533 | {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, 534 | {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, 535 | {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, 536 | {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, 537 | {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, 538 | ] 539 | certifi = [ 540 | {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, 541 | {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, 542 | ] 543 | charset-normalizer = [ 544 | {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, 545 | {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, 546 | ] 547 | click = [ 548 | {file = "click-8.1.2-py3-none-any.whl", hash = "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e"}, 549 | {file = "click-8.1.2.tar.gz", hash = "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"}, 550 | ] 551 | colorama = [ 552 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 553 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 554 | ] 555 | commonmark = [ 556 | {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, 557 | {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, 558 | ] 559 | filelock = [ 560 | {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, 561 | {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, 562 | ] 563 | flake8 = [ 564 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 565 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 566 | ] 567 | idna = [ 568 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 569 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 570 | ] 571 | importlib-metadata = [ 572 | {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, 573 | {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, 574 | ] 575 | iniconfig = [ 576 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 577 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 578 | ] 579 | isort = [ 580 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 581 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 582 | ] 583 | mccabe = [ 584 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 585 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 586 | ] 587 | mypy = [ 588 | {file = "mypy-0.942-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5bf44840fb43ac4074636fd47ee476d73f0039f4f54e86d7265077dc199be24d"}, 589 | {file = "mypy-0.942-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dcd955f36e0180258a96f880348fbca54ce092b40fbb4b37372ae3b25a0b0a46"}, 590 | {file = "mypy-0.942-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6776e5fa22381cc761df53e7496a805801c1a751b27b99a9ff2f0ca848c7eca0"}, 591 | {file = "mypy-0.942-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:edf7237137a1a9330046dbb14796963d734dd740a98d5e144a3eb1d267f5f9ee"}, 592 | {file = "mypy-0.942-cp310-cp310-win_amd64.whl", hash = "sha256:64235137edc16bee6f095aba73be5334677d6f6bdb7fa03cfab90164fa294a17"}, 593 | {file = "mypy-0.942-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b840cfe89c4ab6386c40300689cd8645fc8d2d5f20101c7f8bd23d15fca14904"}, 594 | {file = "mypy-0.942-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b184db8c618c43c3a31b32ff00cd28195d39e9c24e7c3b401f3db7f6e5767f5"}, 595 | {file = "mypy-0.942-cp36-cp36m-win_amd64.whl", hash = "sha256:1a0459c333f00e6a11cbf6b468b870c2b99a906cb72d6eadf3d1d95d38c9352c"}, 596 | {file = "mypy-0.942-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4c3e497588afccfa4334a9986b56f703e75793133c4be3a02d06a3df16b67a58"}, 597 | {file = "mypy-0.942-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f6ad963172152e112b87cc7ec103ba0f2db2f1cd8997237827c052a3903eaa6"}, 598 | {file = "mypy-0.942-cp37-cp37m-win_amd64.whl", hash = "sha256:0e2dd88410937423fba18e57147dd07cd8381291b93d5b1984626f173a26543e"}, 599 | {file = "mypy-0.942-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:246e1aa127d5b78488a4a0594bd95f6d6fb9d63cf08a66dafbff8595d8891f67"}, 600 | {file = "mypy-0.942-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d8d3ba77e56b84cd47a8ee45b62c84b6d80d32383928fe2548c9a124ea0a725c"}, 601 | {file = "mypy-0.942-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2bc249409a7168d37c658e062e1ab5173300984a2dada2589638568ddc1db02b"}, 602 | {file = "mypy-0.942-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9521c1265ccaaa1791d2c13582f06facf815f426cd8b07c3a485f486a8ffc1f3"}, 603 | {file = "mypy-0.942-cp38-cp38-win_amd64.whl", hash = "sha256:e865fec858d75b78b4d63266c9aff770ecb6a39dfb6d6b56c47f7f8aba6baba8"}, 604 | {file = "mypy-0.942-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ce34a118d1a898f47def970a2042b8af6bdcc01546454726c7dd2171aa6dfca"}, 605 | {file = "mypy-0.942-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:10daab80bc40f84e3f087d896cdb53dc811a9f04eae4b3f95779c26edee89d16"}, 606 | {file = "mypy-0.942-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3841b5433ff936bff2f4dc8d54cf2cdbfea5d8e88cedfac45c161368e5770ba6"}, 607 | {file = "mypy-0.942-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f7106cbf9cc2f403693bf50ed7c9fa5bb3dfa9007b240db3c910929abe2a322"}, 608 | {file = "mypy-0.942-cp39-cp39-win_amd64.whl", hash = "sha256:7742d2c4e46bb5017b51c810283a6a389296cda03df805a4f7869a6f41246534"}, 609 | {file = "mypy-0.942-py3-none-any.whl", hash = "sha256:a1b383fe99678d7402754fe90448d4037f9512ce70c21f8aee3b8bf48ffc51db"}, 610 | {file = "mypy-0.942.tar.gz", hash = "sha256:17e44649fec92e9f82102b48a3bf7b4a5510ad0cd22fa21a104826b5db4903e2"}, 611 | ] 612 | mypy-extensions = [ 613 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 614 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 615 | ] 616 | orjson = [ 617 | {file = "orjson-3.6.8-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:3a287a650458de2211db03681b71c3e5cb2212b62f17a39df8ad99fc54855d0f"}, 618 | {file = "orjson-3.6.8-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:5204e25c12cea58e524fc82f7c27ed0586f592f777b33075a92ab7b3eb3687c2"}, 619 | {file = "orjson-3.6.8-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77e8386393add64f959c044e0fb682364fd0e611a6f477aa13f0e6a733bd6a28"}, 620 | {file = "orjson-3.6.8-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:279f2d2af393fdf8601020744cb206b91b54ad60fb8401e0761819c7bda1f4e4"}, 621 | {file = "orjson-3.6.8-cp310-cp310-manylinux_2_24_x86_64.whl", hash = "sha256:c31c9f389be7906f978ed4192eb58a4b74a37ad60556a0b88ddc47c576697770"}, 622 | {file = "orjson-3.6.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0db5c5a0c5b89f092d52f6e5a3701660a9d6ffa9e2968b3ce17c2bc4f5eb0414"}, 623 | {file = "orjson-3.6.8-cp310-none-win_amd64.whl", hash = "sha256:eb22485847b9a0c4bbedc668df860126ac931edbed1d456cf41a59f3cb961ed8"}, 624 | {file = "orjson-3.6.8-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:1a5fe569310bc819279bd4d5f2c349910b104ed3207936246dd5d5e0b085e74a"}, 625 | {file = "orjson-3.6.8-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:ccb356a47ab1067cd3549847e9db1d279a63fe0482d315b3ffd6e7abef35ef77"}, 626 | {file = "orjson-3.6.8-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab29c069c222248ce302a25855b4e1664f9436e8ae5a131fb0859daf31676d2b"}, 627 | {file = "orjson-3.6.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d2b5e4cba9e774ac011071d9d27760f97f4b8cd46003e971d122e712f971345"}, 628 | {file = "orjson-3.6.8-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:c311ec504414d22834d5b972a209619925b48263856a11a14d90230f9682d49c"}, 629 | {file = "orjson-3.6.8-cp37-cp37m-manylinux_2_24_x86_64.whl", hash = "sha256:a3dfec7950b90fb8d143743503ee53fa06b32e6068bdea792fc866284da3d71d"}, 630 | {file = "orjson-3.6.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b890dbbada2cbb26eb29bd43a848426f007f094bb0758df10dfe7a438e1cb4b4"}, 631 | {file = "orjson-3.6.8-cp37-none-win_amd64.whl", hash = "sha256:9143ae2c52771525be9ad11a7a8cc8e7fd75391b107e7e644a9e0050496f6b4f"}, 632 | {file = "orjson-3.6.8-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:33a82199fd42f6436f833e210ae5129c922a5c355629356ca7a8e82964da7285"}, 633 | {file = "orjson-3.6.8-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:90159ea8b9a5a2a98fa33dc7b421cfac4d2ae91ba5e1058f5909e7f059f6b467"}, 634 | {file = "orjson-3.6.8-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:656fbe15d9ef0733e740d9def78f4fdb4153102f4836ee774a05123499005931"}, 635 | {file = "orjson-3.6.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7be3be6153843e0f01351b1313a5ad4723595427680dac2dfff22a37e652ce02"}, 636 | {file = "orjson-3.6.8-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:dd24f66b6697ee7424f7da575ec6cbffc8ede441114d53470949cda4d97c6e56"}, 637 | {file = "orjson-3.6.8-cp38-cp38-manylinux_2_24_x86_64.whl", hash = "sha256:b07c780f7345ecf5901356dc21dee0669defc489c38ce7b9ab0f5e008cc0385c"}, 638 | {file = "orjson-3.6.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ea32015a5d8a4ce00d348a0de5dc7040e0ad58f970a8fcbb5713a1eac129e493"}, 639 | {file = "orjson-3.6.8-cp38-none-win_amd64.whl", hash = "sha256:c5a3e382194c838988ec128a26b08aa92044e5e055491cc4056142af0c1c54d7"}, 640 | {file = "orjson-3.6.8-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:83a8424e857ae1bf53530e88b4eb2f16ca2b489073b924e655f1575cacd7f52a"}, 641 | {file = "orjson-3.6.8-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:81e1a6a2d67f15007dadacbf9ba5d3d79237e5e33786c028557fe5a2b72f1c9a"}, 642 | {file = "orjson-3.6.8-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:137b539881c77866eba86ff6a11df910daf2eb9ab8f1acae62f879e83d7c38af"}, 643 | {file = "orjson-3.6.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cbd358f3b3ad539a27e36900e8e7d172d0e1b72ad9dd7d69544dcbc0f067ee7"}, 644 | {file = "orjson-3.6.8-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:6ab94701542d40b90903ecfc339333f458884979a01cb9268bc662cc67a5f6d8"}, 645 | {file = "orjson-3.6.8-cp39-cp39-manylinux_2_24_x86_64.whl", hash = "sha256:32b6f26593a9eb606b40775826beb0dac152e3d224ea393688fced036045a821"}, 646 | {file = "orjson-3.6.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:afd9e329ebd3418cac3cd747769b1d52daa25fa672bbf414ab59f0e0881b32b9"}, 647 | {file = "orjson-3.6.8-cp39-none-win_amd64.whl", hash = "sha256:0c89b419914d3d1f65a1b0883f377abe42a6e44f6624ba1c63e8846cbfc2fa60"}, 648 | {file = "orjson-3.6.8.tar.gz", hash = "sha256:e19d23741c5de13689bb316abfccea15a19c264e3ec8eb332a5319a583595ace"}, 649 | ] 650 | packaging = [ 651 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 652 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 653 | ] 654 | pathspec = [ 655 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 656 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 657 | ] 658 | platformdirs = [ 659 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 660 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 661 | ] 662 | pluggy = [ 663 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 664 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 665 | ] 666 | py = [ 667 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 668 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 669 | ] 670 | pycodestyle = [ 671 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 672 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 673 | ] 674 | pyflakes = [ 675 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 676 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 677 | ] 678 | pygments = [ 679 | {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, 680 | {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, 681 | ] 682 | pyparsing = [ 683 | {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, 684 | {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, 685 | ] 686 | pytest = [ 687 | {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, 688 | {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, 689 | ] 690 | pytest-black = [ 691 | {file = "pytest-black-0.3.12.tar.gz", hash = "sha256:1d339b004f764d6cd0f06e690f6dd748df3d62e6fe1a692d6a5500ac2c5b75a5"}, 692 | ] 693 | pytest-flake8 = [ 694 | {file = "pytest-flake8-1.1.1.tar.gz", hash = "sha256:ba4f243de3cb4c2486ed9e70752c80dd4b636f7ccb27d4eba763c35ed0cd316e"}, 695 | {file = "pytest_flake8-1.1.1-py2.py3-none-any.whl", hash = "sha256:e0661a786f8cbf976c185f706fdaf5d6df0b1667c3bcff8e823ba263618627e7"}, 696 | ] 697 | pytest-isort = [ 698 | {file = "pytest-isort-2.0.0.tar.gz", hash = "sha256:821a8c5c9c4f3a3c52cfa9c541fbe89ac9e28728125125af53724c4c3f129117"}, 699 | {file = "pytest_isort-2.0.0-py3-none-any.whl", hash = "sha256:ab949c593213dad38ba75db32a0ce361fcddd11d4152be4a2c93b85104cc4376"}, 700 | ] 701 | pytest-mypy = [ 702 | {file = "pytest-mypy-0.8.1.tar.gz", hash = "sha256:1fa55723a4bf1d054fcba1c3bd694215a2a65cc95ab10164f5808afd893f3b11"}, 703 | {file = "pytest_mypy-0.8.1-py3-none-any.whl", hash = "sha256:6e68e8eb7ceeb7d1c83a1590912f784879f037b51adfb9c17b95c6b2fc57466b"}, 704 | ] 705 | requests = [ 706 | {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, 707 | {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, 708 | ] 709 | rich = [ 710 | {file = "rich-12.2.0-py3-none-any.whl", hash = "sha256:c50f3d253bc6a9bb9c79d61a26d510d74abdf1b16881260fab5edfc3edfb082f"}, 711 | {file = "rich-12.2.0.tar.gz", hash = "sha256:ea74bc9dad9589d8eea3e3fd0b136d8bf6e428888955f215824c2894f0da8b47"}, 712 | ] 713 | toml = [ 714 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 715 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 716 | ] 717 | tomli = [ 718 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 719 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 720 | ] 721 | typed-ast = [ 722 | {file = "typed_ast-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ad3b48cf2b487be140072fb86feff36801487d4abb7382bb1929aaac80638ea"}, 723 | {file = "typed_ast-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:542cd732351ba8235f20faa0fc7398946fe1a57f2cdb289e5497e1e7f48cfedb"}, 724 | {file = "typed_ast-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc2c11ae59003d4a26dda637222d9ae924387f96acae9492df663843aefad55"}, 725 | {file = "typed_ast-1.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd5df1313915dbd70eaaa88c19030b441742e8b05e6103c631c83b75e0435ccc"}, 726 | {file = "typed_ast-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:e34f9b9e61333ecb0f7d79c21c28aa5cd63bec15cb7e1310d7d3da6ce886bc9b"}, 727 | {file = "typed_ast-1.5.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f818c5b81966d4728fec14caa338e30a70dfc3da577984d38f97816c4b3071ec"}, 728 | {file = "typed_ast-1.5.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3042bfc9ca118712c9809201f55355479cfcdc17449f9f8db5e744e9625c6805"}, 729 | {file = "typed_ast-1.5.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4fff9fdcce59dc61ec1b317bdb319f8f4e6b69ebbe61193ae0a60c5f9333dc49"}, 730 | {file = "typed_ast-1.5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8e0b8528838ffd426fea8d18bde4c73bcb4167218998cc8b9ee0a0f2bfe678a6"}, 731 | {file = "typed_ast-1.5.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ef1d96ad05a291f5c36895d86d1375c0ee70595b90f6bb5f5fdbee749b146db"}, 732 | {file = "typed_ast-1.5.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed44e81517364cb5ba367e4f68fca01fba42a7a4690d40c07886586ac267d9b9"}, 733 | {file = "typed_ast-1.5.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f60d9de0d087454c91b3999a296d0c4558c1666771e3460621875021bf899af9"}, 734 | {file = "typed_ast-1.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9e237e74fd321a55c90eee9bc5d44be976979ad38a29bbd734148295c1ce7617"}, 735 | {file = "typed_ast-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ee852185964744987609b40aee1d2eb81502ae63ee8eef614558f96a56c1902d"}, 736 | {file = "typed_ast-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:27e46cdd01d6c3a0dd8f728b6a938a6751f7bd324817501c15fb056307f918c6"}, 737 | {file = "typed_ast-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d64dabc6336ddc10373922a146fa2256043b3b43e61f28961caec2a5207c56d5"}, 738 | {file = "typed_ast-1.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8cdf91b0c466a6c43f36c1964772918a2c04cfa83df8001ff32a89e357f8eb06"}, 739 | {file = "typed_ast-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:9cc9e1457e1feb06b075c8ef8aeb046a28ec351b1958b42c7c31c989c841403a"}, 740 | {file = "typed_ast-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e20d196815eeffb3d76b75223e8ffed124e65ee62097e4e73afb5fec6b993e7a"}, 741 | {file = "typed_ast-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:37e5349d1d5de2f4763d534ccb26809d1c24b180a477659a12c4bde9dd677d74"}, 742 | {file = "typed_ast-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f1a27592fac87daa4e3f16538713d705599b0a27dfe25518b80b6b017f0a6d"}, 743 | {file = "typed_ast-1.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8831479695eadc8b5ffed06fdfb3e424adc37962a75925668deeb503f446c0a3"}, 744 | {file = "typed_ast-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:20d5118e494478ef2d3a2702d964dae830aedd7b4d3b626d003eea526be18718"}, 745 | {file = "typed_ast-1.5.3.tar.gz", hash = "sha256:27f25232e2dd0edfe1f019d6bfaaf11e86e657d9bdb7b0956db95f560cceb2b3"}, 746 | ] 747 | types-orjson = [ 748 | {file = "types-orjson-3.6.2.tar.gz", hash = "sha256:cf9afcc79a86325c7aff251790338109ed6f6b1bab09d2d4262dd18c85a3c638"}, 749 | {file = "types_orjson-3.6.2-py3-none-any.whl", hash = "sha256:22ee9a79236b6b0bfb35a0684eded62ad930a88a56797fa3c449b026cf7dbfe4"}, 750 | ] 751 | types-requests = [ 752 | {file = "types-requests-2.27.20.tar.gz", hash = "sha256:63344573cde6c4efd44d867c0158d9fb7e6beb95721cbe9882f3f857ee8a5398"}, 753 | {file = "types_requests-2.27.20-py3-none-any.whl", hash = "sha256:68b8de86552116424ec23b77afc925e111afb6496d3821b183b7d151b3b834d4"}, 754 | ] 755 | types-urllib3 = [ 756 | {file = "types-urllib3-1.26.13.tar.gz", hash = "sha256:40f8fb5e8cd7d57e8aefdee3fdd5e930aa1a1bb4179cdadd55226cea588af790"}, 757 | {file = "types_urllib3-1.26.13-py3-none-any.whl", hash = "sha256:ff7500641824f881b2c7bde4cc57e6c3abf03d1e005bae83aca752e77213a5da"}, 758 | ] 759 | typing-extensions = [ 760 | {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, 761 | {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, 762 | ] 763 | urllib3 = [ 764 | {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, 765 | {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, 766 | ] 767 | zipp = [ 768 | {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, 769 | {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, 770 | ] 771 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pywhat" 3 | version = "5.1.0" 4 | description = "What is that thing?" 5 | authors = ["Bee "] 6 | license = "MIT" 7 | documentation = "https://github.com/bee-san/pyWhat/wiki" 8 | readme = "README.md" 9 | 10 | [tool.poetry.dependencies] 11 | python = "^3.7" 12 | click = "^8.0.0" 13 | rich = ">=11.0.0" 14 | orjson = {version = "^3.6.1", optional = true} 15 | 16 | [tool.poetry.dev-dependencies] 17 | pytest = "^7.0" 18 | black = "^22.1.0" 19 | isort = "^5.9.3" 20 | flake8 = "^4.0" 21 | requests = "^2.26.0" 22 | pytest-black = "^0.3.12" 23 | pytest-isort = "^2.0.0" 24 | pytest-flake8 = "^1.0.7" 25 | pytest-mypy = "^0.8.1" 26 | types-requests = "^2.25.9" 27 | types-orjson = "^3.6.0" 28 | 29 | [tool.poetry.extras] 30 | optimize = ["orjson"] 31 | 32 | [build-system] 33 | requires = ["poetry-core>=1.0.0"] 34 | build-backend = "poetry.core.masonry.api" 35 | 36 | [tool.poetry.scripts] 37 | pywhat = "pywhat.what:main" 38 | what = "pywhat.what:main" 39 | 40 | [tool.isort] 41 | profile = "black" 42 | multi_line_output = 3 43 | include_trailing_comma = true 44 | force_grid_wrap = 0 45 | use_parentheses = true 46 | ensure_newline_before_comments = true 47 | line_length = 88 48 | 49 | [tool.mypy] 50 | ignore_missing_imports = true 51 | 52 | [tool.pytest.ini_options] 53 | addopts = "--black --isort --flake8 --mypy" 54 | -------------------------------------------------------------------------------- /pywhat/Data/file_signatures.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Hexadecimal File Signature": "2321", 4 | "ISO 8859-1": "23 21", 5 | "Filename Extension": null, 6 | "Description": "Script or data to be passed to the program following the Shebang (Unix)shebang (#!)", 7 | "Popular": 0 8 | }, 9 | { 10 | "Hexadecimal File Signature": "a1b2c3d4d4c3b2a1", 11 | "ISO 8859-1": "a1 b2 c3 d4 d4 c3 b2 a1", 12 | "Filename Extension": "pcap", 13 | "URL": "https://wiki.wireshark.org/Development/LibpcapFileFormat#Global_Header", 14 | "Description": "Libpcap File Format", 15 | "Popular": 0 16 | }, 17 | { 18 | "Hexadecimal File Signature": "0a0d0d0a", 19 | "ISO 8859-1": null, 20 | "Filename Extension": "pcapng", 21 | "URL": "https://www.winpcap.org/ntar/draft/PCAP-DumpFileFormat.html#sectionshb ", 22 | "Description": "PCAP Next Generation Dump File Format", 23 | "Popular": 0 24 | }, 25 | { 26 | "Hexadecimal File Signature": "edabeedb", 27 | "ISO 8859-1": "ed ab ee db", 28 | "Filename Extension": "rpm", 29 | "URL": "ftp://ftp.tuwien.ac.at/.vhost/www.openpkg.org/doc/book/maximum-rpm.html/node26.html ", 30 | "Description": "RedHat Package Manager (RPM) package", 31 | "Popular": 0 32 | }, 33 | { 34 | "Hexadecimal File Signature": "53514c69746520666f726d6174203300", 35 | "ISO 8859-1": "SQLite format 3.", 36 | "Filename Extension": "sqlitedbsqlitedb", 37 | "URL": "https://www.sqlite.org/fileformat.html ", 38 | "Description": "SQLite Database", 39 | "Popular": 0 40 | }, 41 | { 42 | "Hexadecimal File Signature": "53503031", 43 | "ISO 8859-1": "SP01", 44 | "Filename Extension": "bin", 45 | "URL": "https://github.com/NiLuJe/KindleTool ", 46 | "Description": "Amazon Kindle Update Package", 47 | "Popular": 0 48 | }, 49 | { 50 | "Hexadecimal File Signature": "00", 51 | "ISO 8859-1": null, 52 | "Filename Extension": "PICPIFSEAYTR", 53 | "Description": "IBM Storyboard bitmap file Windows Program information fileProgram Information File Mac StuffItStuffit Self-Extracting Archive IRIS Optical character recognitionOCR data file", 54 | "Popular": 0 55 | }, 56 | { 57 | "Hexadecimal File Signature": "000000000000000000000000000000000000000000000000", 58 | "ISO 8859-1": null, 59 | "Filename Extension": "PDB", 60 | "Description": "PalmPilot Database/Document File", 61 | "Popular": 0 62 | }, 63 | { 64 | "Hexadecimal File Signature": "BEBAFECA", 65 | "ISO 8859-1": "BE BA FE CA", 66 | "Filename Extension": "DBA", 67 | "Description": "Palm (PDA)Palm Desktop Calendar Archive", 68 | "Popular": 0 69 | }, 70 | { 71 | "Hexadecimal File Signature": "00014244", 72 | "ISO 8859-1": "..BD", 73 | "Filename Extension": "DBA", 74 | "Description": "Palm Desktop To Do Archive", 75 | "Popular": 0 76 | }, 77 | { 78 | "Hexadecimal File Signature": "00014454", 79 | "ISO 8859-1": "..DT", 80 | "Filename Extension": "TDA", 81 | "Description": "Palm Desktop Calendar Archive", 82 | "Popular": 0 83 | }, 84 | { 85 | "Hexadecimal File Signature": "54444624", 86 | "ISO 8859-1": "TDF$", 87 | "Filename Extension": "TDF$", 88 | "Description": "Telegram Desktop File", 89 | "Popular": 0 90 | }, 91 | { 92 | "Hexadecimal File Signature": "54444546", 93 | "ISO 8859-1": "TDEF", 94 | "Filename Extension": "TDEF", 95 | "Description": "Telegram Desktop Encrypted File", 96 | "Popular": 0 97 | }, 98 | { 99 | "Hexadecimal File Signature": "00010000", 100 | "ISO 8859-1": "00 01 00 00", 101 | "Filename Extension": null, 102 | "Description": "Palm Desktop Data File (Microsoft AccessAccess format)", 103 | "Popular": 0 104 | }, 105 | { 106 | "Hexadecimal File Signature": "00000100", 107 | "ISO 8859-1": null, 108 | "Filename Extension": "ico", 109 | "Description": "Computer icon encoded in ICO (file format)ICO file format[http://msdn.microsoft.com/en-us/library/ms997538.aspx Icons] (at MSDN)", 110 | "Popular": 0 111 | }, 112 | { 113 | "Hexadecimal File Signature": "667479703367", 114 | "ISO 8859-1": "ftyp3g", 115 | "Filename Extension": "3gp3g2", 116 | "Description": "3rd Generation Partnership Project 3GPP and 3GPP2 multimedia files", 117 | "Popular": 0 118 | }, 119 | { 120 | "Hexadecimal File Signature": "1F9D", 121 | "ISO 8859-1": null, 122 | "Filename Extension": "ztar.z", 123 | "Description": "compressed file (often Tar (file format)tar zip) using Lempel-Ziv-Welch algorithm", 124 | "Popular": 0 125 | }, 126 | { 127 | "Hexadecimal File Signature": "1FA0", 128 | "ISO 8859-1": "1F A0", 129 | "Filename Extension": "ztar.z", 130 | "Description": "Compressed file (often tar zip) using LHA (file format)LZH algorithm", 131 | "Popular": 0 132 | }, 133 | { 134 | "Hexadecimal File Signature": "4241434B4D494B454449534B", 135 | "ISO 8859-1": "BACKMIKE DISK", 136 | "Filename Extension": "bac", 137 | "Description": "File or Magnetic tape data storagetape containing a backup done with AmiBack on an Amiga. It typically is paired with an index file (idx) with the table of contents.", 138 | "Popular": 0 139 | }, 140 | { 141 | "Hexadecimal File Signature": "425A68", 142 | "ISO 8859-1": "BZh", 143 | "Filename Extension": "bz2", 144 | "Description": "Compressed file using Bzip2 algorithm", 145 | "Popular": 0 146 | }, 147 | { 148 | "Hexadecimal File Signature": "474946383761474946383961", 149 | "ISO 8859-1": "GIF87a GIF89a", 150 | "Filename Extension": "gif", 151 | "Description": "Image file encoded in the Graphics Interchange Format (GIF)[http://www.w3.org/Graphics/GIF/spec-gif89a.txt GRAPHICS INTERCHANGE FORMAT(sm) Version 89a]", 152 | "Popular": 0 153 | }, 154 | { 155 | "Hexadecimal File Signature": "49492A00(Endianness#Little-endianlittle-endianformat)4D4D002A(Endianness#Big-endianbig-endianformat)", 156 | "ISO 8859-1": "II*. MM.*", 157 | "Filename Extension": "tiftiff", 158 | "Description": "Tagged Image File Format (TIFF)", 159 | "Popular": 0 160 | }, 161 | { 162 | "Hexadecimal File Signature": "49492A00100000004352", 163 | "ISO 8859-1": "II*..... CR", 164 | "Filename Extension": "cr2", 165 | "URL": "http://filext.com/file-extension/CR2", 166 | "Description": "Canon RAW Format Version 2 weburl", 167 | "Popular": 0 168 | }, 169 | { 170 | "Hexadecimal File Signature": "802A5FD7", 171 | "ISO 8859-1": ".*_.", 172 | "Filename Extension": "cin", 173 | "Description": "Eastman KodakKodak Cineon#Cineon file formatCineon image", 174 | "Popular": 0 175 | }, 176 | { 177 | "Hexadecimal File Signature": "524E4301524E4302", 178 | "ISO 8859-1": "RNC.", 179 | "Filename Extension": null, 180 | "Description": "Compressed file using [http://segaretro.org/Rob_Northen_compression Rob Northen Compression] (version 1 and 2) algorithm", 181 | "Popular": 0 182 | }, 183 | { 184 | "Hexadecimal File Signature": "53445058(Endianness#Big-endianbig-endianformat)58504453(Endianness#Little-endianlittle-endianformat)", 185 | "ISO 8859-1": "SDPX XPDS", 186 | "Filename Extension": "dpx", 187 | "Description": "SMPTE Digital Picture ExchangeDPX image", 188 | "Popular": 0 189 | }, 190 | { 191 | "Hexadecimal File Signature": "762F3101", 192 | "ISO 8859-1": "v/1.", 193 | "Filename Extension": "exr", 194 | "Description": "OpenEXROpenEXR image", 195 | "Popular": 0 196 | }, 197 | { 198 | "Hexadecimal File Signature": "425047FB", 199 | "ISO 8859-1": "BPG\u00fb", 200 | "Filename Extension": "bpg", 201 | "URL": "http://bellard.org/bpg/ ", 202 | "Description": "Better Portable Graphics format", 203 | "Popular": 0 204 | }, 205 | { 206 | "Hexadecimal File Signature": "FFD8FFDBFFD8FFE000104A4649460001FFD8FFEEFFD8FFE1????457869660000", 207 | "ISO 8859-1": "\u00ff\u00d8\u00ff\u00db \u00ff\u00d8\u00ff\u00e0..JFIF.. \u00ff\u00d8\u00ff\u00ee \u00ff\u00d8\u00ff\u00e1..Exif..", 208 | "Filename Extension": "jpgjpeg", 209 | "Description": "JPEG raw or in the JFIF or Exif file format", 210 | "Popular": 0 211 | }, 212 | { 213 | "Hexadecimal File Signature": "464F524D????????494C424D", 214 | "ISO 8859-1": "FORM.... ILBM", 215 | "Filename Extension": "ilbmlbmibmiff", 216 | "Description": "Interchange File FormatIFF ILBMInterleaved Bitmap Image", 217 | "Popular": 0 218 | }, 219 | { 220 | "Hexadecimal File Signature": "464F524D????????38535658", 221 | "ISO 8859-1": "FORM.... 8SVX", 222 | "Filename Extension": "8svx8svsvxsndiff", 223 | "Description": "Interchange File FormatIFF 8SVX8-Bit Sampled Voice", 224 | "Popular": 0 225 | }, 226 | { 227 | "Hexadecimal File Signature": "464F524D????????4143424D", 228 | "ISO 8859-1": "FORM.... ACBM", 229 | "Filename Extension": "acbmiff", 230 | "Description": "Amiga Contiguous Bitmap", 231 | "Popular": 0 232 | }, 233 | { 234 | "Hexadecimal File Signature": "464F524D????????414E424D", 235 | "ISO 8859-1": "FORM.... ANBM", 236 | "Filename Extension": "anbmiff", 237 | "Description": "Interchange File FormatIFF IFF Animated BitmapAnimated Bitmap", 238 | "Popular": 0 239 | }, 240 | { 241 | "Hexadecimal File Signature": "464F524D????????414E494D", 242 | "ISO 8859-1": "FORM.... ANIM", 243 | "Filename Extension": "animiff", 244 | "Description": "Interchange File FormatIFF IFF CEL AnimationCEL Animation", 245 | "Popular": 0 246 | }, 247 | { 248 | "Hexadecimal File Signature": "464F524D????????46415858", 249 | "ISO 8859-1": "FORM.... FAXX", 250 | "Filename Extension": "faxxfaxiff", 251 | "Description": "Interchange File FormatIFF FaxFacsimile Image", 252 | "Popular": 0 253 | }, 254 | { 255 | "Hexadecimal File Signature": "464F524D????????46545854", 256 | "ISO 8859-1": "FORM.... FTXT", 257 | "Filename Extension": "ftxtiff", 258 | "Description": "Interchange File FormatIFF Formatted textFormatted Text", 259 | "Popular": 0 260 | }, 261 | { 262 | "Hexadecimal File Signature": "464F524D????????534D5553", 263 | "ISO 8859-1": "FORM.... SMUS", 264 | "Filename Extension": "smussmumusiff", 265 | "Description": "Interchange File FormatIFF IFF Simple Musical ScoreSimple Musical Score", 266 | "Popular": 0 267 | }, 268 | { 269 | "Hexadecimal File Signature": "464F524D????????434D5553", 270 | "ISO 8859-1": "FORM.... CMUS", 271 | "Filename Extension": "cmusmusiff", 272 | "Description": "Interchange File FormatIFF IFF Musical ScoreMusical Score", 273 | "Popular": 0 274 | }, 275 | { 276 | "Hexadecimal File Signature": "464F524D????????5955564E", 277 | "ISO 8859-1": "FORM.... YUVN", 278 | "Filename Extension": "yuvnyuviff", 279 | "Description": "Interchange File FormatIFF YUVYUV Image", 280 | "Popular": 0 281 | }, 282 | { 283 | "Hexadecimal File Signature": "464F524D????????46414E54", 284 | "ISO 8859-1": "FORM.... FANT", 285 | "Filename Extension": "iff", 286 | "Description": "Amiga FantavisionFantavision Movie", 287 | "Popular": 0 288 | }, 289 | { 290 | "Hexadecimal File Signature": "464F524D????????41494646", 291 | "ISO 8859-1": "FORM.... AIFF", 292 | "Filename Extension": "aiffaifaifcsndiff", 293 | "Description": "Audio Interchange File Format", 294 | "Popular": 0 295 | }, 296 | { 297 | "Hexadecimal File Signature": "494E4458", 298 | "ISO 8859-1": "INDX", 299 | "Filename Extension": "idx", 300 | "Description": "Index file to a file or Magnetic tape data storagetape containing a backup done with AmiBack on an Amiga.", 301 | "Popular": 0 302 | }, 303 | { 304 | "Hexadecimal File Signature": "4C5A4950", 305 | "ISO 8859-1": "LZIP", 306 | "Filename Extension": "lz", 307 | "Description": "lzip compressed file", 308 | "Popular": 0 309 | }, 310 | { 311 | "Hexadecimal File Signature": "4D5A", 312 | "ISO 8859-1": null, 313 | "Filename Extension": "exedll", 314 | "Description": "DOS MZ executable file format and its descendants (including New ExecutableNE and Portable ExecutablePE)", 315 | "Popular": 0 316 | }, 317 | { 318 | "Hexadecimal File Signature": "504B0304504B0506(emptyarchive)504B0708(spannedarchive)", 319 | "ISO 8859-1": "PK..", 320 | "Filename Extension": "zipaarapkdocxepub.ipaipajarkmzMozilla Archive Formatmaffodpodsodtpk3pk4pptxusdzvsdxxlsxXPInstallxpi", 321 | "Description": "ZIP (file format)zip file format and formats based on it, such as EPUB, JAR (file format)JAR, OpenDocumentODF, Office Open XMLOOXML", 322 | "Popular": 0 323 | }, 324 | { 325 | "Hexadecimal File Signature": "526172211A0700", 326 | "ISO 8859-1": "Rar!...", 327 | "Filename Extension": "rar", 328 | "Description": "RAR (file format)RAR archive version 1.50 onwards web title=TechNote.txt: RAR version 4.00 - Technical information date=2010-12-01 quote=The marker block is actually considered as a fixed byte sequence: 0x52 0x61 0x72 0x21 0x1a 0x07 0x00", 329 | "Popular": 0 330 | }, 331 | { 332 | "Hexadecimal File Signature": "526172211A070100", 333 | "ISO 8859-1": "Rar!....", 334 | "Filename Extension": "rar", 335 | "URL": "http://www.rarlab.com/technote.htm ", 336 | "Description": "RAR (file format)RAR archive version 5.0 onwards", 337 | "Popular": 0 338 | }, 339 | { 340 | "Hexadecimal File Signature": "5A4D", 341 | "ISO 8859-1": null, 342 | "Filename Extension": "exe", 343 | "Description": "DOS ZM executable file format and its descendants (rare)", 344 | "Popular": 0 345 | }, 346 | { 347 | "Hexadecimal File Signature": "7F454C46", 348 | "ISO 8859-1": ".ELF", 349 | "Filename Extension": null, 350 | "Description": "Executable and Linkable Format", 351 | "Popular": 0 352 | }, 353 | { 354 | "Hexadecimal File Signature": "89504E470D0A1A0A", 355 | "ISO 8859-1": ".PNG....", 356 | "Filename Extension": "png", 357 | "URL": "http://tools.ietf.org/html/rfc2083", 358 | "Description": "Image encoded in the Portable Network Graphics format weburl", 359 | "Popular": 0 360 | }, 361 | { 362 | "Hexadecimal File Signature": "C9", 363 | "ISO 8859-1": null, 364 | "Filename Extension": "com", 365 | "Description": "COM file (CP/M)CP/M 3 and higher with overlays", 366 | "Popular": 0 367 | }, 368 | { 369 | "Hexadecimal File Signature": "CAFEBABE", 370 | "ISO 8859-1": "\u00ca\u00fe\u00ba\u00be", 371 | "Filename Extension": "class", 372 | "Description": "Java class file, Fat binary#AppleMach-O Fat Binary", 373 | "Popular": 0 374 | }, 375 | { 376 | "Hexadecimal File Signature": "EFBBBF", 377 | "ISO 8859-1": "\u00ef\u00bb\u00bf", 378 | "Filename Extension": null, 379 | "Description": "UTF-8 encoded Unicode byte order mark, commonly seen in text files.", 380 | "Popular": 0 381 | }, 382 | { 383 | "Hexadecimal File Signature": "FEEDFACE", 384 | "ISO 8859-1": null, 385 | "Filename Extension": null, 386 | "Description": "Mach-O binary (32-bit)", 387 | "Popular": 0 388 | }, 389 | { 390 | "Hexadecimal File Signature": "FEEDFACF", 391 | "ISO 8859-1": null, 392 | "Filename Extension": null, 393 | "Description": "Mach-O binary (64-bit)", 394 | "Popular": 0 395 | }, 396 | { 397 | "Hexadecimal File Signature": "FEEDFEED", 398 | "ISO 8859-1": null, 399 | "Filename Extension": null, 400 | "Description": "JKS [http://hg.openjdk.java.net/jdk10/jdk10/jdk/file/777356696811/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java JavakeyStore]", 401 | "Popular": 0 402 | }, 403 | { 404 | "Hexadecimal File Signature": "CEFAEDFE", 405 | "ISO 8859-1": null, 406 | "Filename Extension": null, 407 | "URL": "\"apple.com\">{{cite web", 408 | "Description": "Mach-O binary (reverse byte ordering scheme, 32-bit)ref name", 409 | "Popular": 0 410 | }, 411 | { 412 | "Hexadecimal File Signature": "CFFAEDFE", 413 | "ISO 8859-1": null, 414 | "Filename Extension": null, 415 | "Description": "Mach-O binary (reverse byte ordering scheme, 64-bit)", 416 | "Popular": 0 417 | }, 418 | { 419 | "Hexadecimal File Signature": "FFFE", 420 | "ISO 8859-1": null, 421 | "Filename Extension": null, 422 | "Description": "Byte-order mark for text file encoded in Endianness#Little-endianlittle-endian UTF-1616-bit Unicode Transfer Format", 423 | "Popular": 0 424 | }, 425 | { 426 | "Hexadecimal File Signature": "FFFE0000", 427 | "ISO 8859-1": null, 428 | "Filename Extension": null, 429 | "Description": "Byte-order mark for text file encoded in little-endian UTF-3232-bit Unicode Transfer Format", 430 | "Popular": 0 431 | }, 432 | { 433 | "Hexadecimal File Signature": "25215053", 434 | "ISO 8859-1": "%!PS", 435 | "Filename Extension": "ps", 436 | "Description": "PostScriptPostScript document", 437 | "Popular": 0 438 | }, 439 | { 440 | "Hexadecimal File Signature": "495453460300000060000000", 441 | "ISO 8859-1": "ITSF....`...", 442 | "Filename Extension": "chm", 443 | "Description": "Microsoft_Compiled_HTML_HelpMS Windows HtmlHelp Data", 444 | "Popular": 0 445 | }, 446 | { 447 | "Hexadecimal File Signature": "255044462d", 448 | "ISO 8859-1": "%PDF-", 449 | "Filename Extension": "pdf", 450 | "URL": "https://github.com/file/file/blob/master/magic/Magdir/pdf", 451 | "Description": "Portable Document FormatPDF document", 452 | "Popular": 0 453 | }, 454 | { 455 | "Hexadecimal File Signature": "3026B2758E66CF11A6D900AA0062CE6C", 456 | "ISO 8859-1": "0&\u00b2u.f\u00cf .\u00a6\u00d9.\u00aa.b\u00cel", 457 | "Filename Extension": "asfwmawmv", 458 | "URL": "http://www.digitalpreservation.gov/formats/fdd/fdd000067.shtml ", 459 | "Description": "Advanced Systems Format", 460 | "Popular": 0 461 | }, 462 | { 463 | "Hexadecimal File Signature": "2453444930303031", 464 | "ISO 8859-1": "$SDI0001", 465 | "Filename Extension": null, 466 | "Description": "System Deployment Image, a disk image format used by Microsoft", 467 | "Popular": 0 468 | }, 469 | { 470 | "Hexadecimal File Signature": "4F676753", 471 | "ISO 8859-1": "OggS", 472 | "Filename Extension": "oggogaogv", 473 | "Description": "Ogg, an Open-source licenseopen source media container format", 474 | "Popular": 0 475 | }, 476 | { 477 | "Hexadecimal File Signature": "38425053", 478 | "ISO 8859-1": "8BPS", 479 | "Filename Extension": "psd", 480 | "Description": "Photoshop Document file, Adobe Photoshop's native file format", 481 | "Popular": 0 482 | }, 483 | { 484 | "Hexadecimal File Signature": "52494646????????57415645", 485 | "ISO 8859-1": "RIFF.... WAVE", 486 | "Filename Extension": "wav", 487 | "Description": "Waveform Audio File Format", 488 | "Popular": 0 489 | }, 490 | { 491 | "Hexadecimal File Signature": "52494646????????41564920", 492 | "ISO 8859-1": "RIFF.... AVI.", 493 | "Filename Extension": "avi", 494 | "Description": "Audio Video Interleave video format", 495 | "Popular": 0 496 | }, 497 | { 498 | "Hexadecimal File Signature": "FFFBFFF3FFF2", 499 | "ISO 8859-1": "\u00ff\u00fb \u00ff\u00f3 \u00ff\u00f2", 500 | "Filename Extension": "mp3", 501 | "Description": "MPEG-1 Layer 3 file without an ID3 tag or with an ID3v1 tag (which's appended at the end of the file)", 502 | "Popular": 0 503 | }, 504 | { 505 | "Hexadecimal File Signature": "494433", 506 | "ISO 8859-1": "ID3", 507 | "Filename Extension": "mp3", 508 | "Description": "MP3 file with an ID3v2 container", 509 | "Popular": 0 510 | }, 511 | { 512 | "Hexadecimal File Signature": "424D", 513 | "ISO 8859-1": null, 514 | "Filename Extension": "bmpdib", 515 | "Description": "BMP file formatBMP file, a bitmap format used mostly in the Windows world", 516 | "Popular": 0 517 | }, 518 | { 519 | "Hexadecimal File Signature": "4344303031", 520 | "ISO 8859-1": "CD001", 521 | "Filename Extension": "iso", 522 | "URL": "http://www.garykessler.net/library/file_sigs.html", 523 | "Description": "ISO9660 CD/DVD image file weburl", 524 | "Popular": 0 525 | }, 526 | { 527 | "Hexadecimal File Signature": "53494D504C4520203D202020202020202020202020202020202020202054", 528 | "ISO 8859-1": "SIMPLE = T", 529 | "Filename Extension": "fits", 530 | "URL": "http://www.digitalpreservation.gov/formats/fdd/fdd000317.shtml#sign", 531 | "Description": "Flexible Image Transport System (FITS) weburl", 532 | "Popular": 0 533 | }, 534 | { 535 | "Hexadecimal File Signature": "664C6143", 536 | "ISO 8859-1": "fLaC", 537 | "Filename Extension": "flac", 538 | "URL": "http://flac.sourceforge.net/format.html#stream ", 539 | "Description": "Free Lossless Audio Codec weburl", 540 | "Popular": 0 541 | }, 542 | { 543 | "Hexadecimal File Signature": "4D546864", 544 | "ISO 8859-1": "MThd", 545 | "Filename Extension": "midmidi", 546 | "URL": "http://filesignatures.net/index.php?search", 547 | "Description": "MIDI#Standard MIDI filesMIDI sound file weburl", 548 | "Popular": 0 549 | }, 550 | { 551 | "Hexadecimal File Signature": "D0CF11E0A1B11AE1", 552 | "ISO 8859-1": null, 553 | "Filename Extension": "docxlspptmsg", 554 | "URL": "http://social.msdn.microsoft.com/Forums/en-US/343d09e3-5fdf-4b4a-9fa6-8ccb37a35930/developing-a-tool-to-recognise-ms-office-file-types-doc-xls-mdb-ppt-?forum", 555 | "Description": "Compound File Binary Format, a container format used for document by older versions of Microsoft Office. weburl", 556 | "Popular": 0 557 | }, 558 | { 559 | "Hexadecimal File Signature": "6465780A30333500", 560 | "ISO 8859-1": "dex.035.", 561 | "Filename Extension": "dex", 562 | "Description": "Dalvik (software)Dalvik Executable", 563 | "Popular": 0 564 | }, 565 | { 566 | "Hexadecimal File Signature": "4B444D", 567 | "ISO 8859-1": "KDM", 568 | "Filename Extension": "vmdk", 569 | "URL": "https://www.vmware.com/support/ws55/doc/ws_learning_files_in_a_vm.html", 570 | "Description": "VMDK files weburl", 571 | "Popular": 0 572 | }, 573 | { 574 | "Hexadecimal File Signature": "43723234", 575 | "ISO 8859-1": "Cr24", 576 | "Filename Extension": "crx", 577 | "URL": "http://developer.chrome.com/extensions/crx.html", 578 | "Description": "Google Chrome extension weburl", 579 | "Popular": 0 580 | }, 581 | { 582 | "Hexadecimal File Signature": "41474433", 583 | "ISO 8859-1": "AGD3", 584 | "Filename Extension": "fh8", 585 | "URL": "https://mail.python.org/pipermail/pythonmac-sig/2005-February/013028.html", 586 | "Description": "Adobe FreeHandFreeHand 8 document weburl", 587 | "Popular": 0 588 | }, 589 | { 590 | "Hexadecimal File Signature": "05070000424F424F0507000000000000000000000001", 591 | "ISO 8859-1": "....BOBO ........ ....", 592 | "Filename Extension": "cwk", 593 | "Description": "AppleWorks 5 document", 594 | "Popular": 0 595 | }, 596 | { 597 | "Hexadecimal File Signature": "0607E100424F424F0607E10000000000000000000001", 598 | "ISO 8859-1": "....BOBO ........ ....", 599 | "Filename Extension": "cwk", 600 | "Description": "AppleWorks 6 document", 601 | "Popular": 0 602 | }, 603 | { 604 | "Hexadecimal File Signature": "4552020000008B455202000000", 605 | "ISO 8859-1": "ER.... \u00e3ER....", 606 | "Filename Extension": "toast", 607 | "Description": "Roxio Roxio ToastToast disc image file, also some .dmg-files begin with same bytes", 608 | "Popular": 0 609 | }, 610 | { 611 | "Hexadecimal File Signature": "7801730D626260", 612 | "ISO 8859-1": "x.s.bb`", 613 | "Filename Extension": "dmg", 614 | "Description": "Apple Disk Image file", 615 | "Popular": 0 616 | }, 617 | { 618 | "Hexadecimal File Signature": "78617221", 619 | "ISO 8859-1": "xar!", 620 | "Filename Extension": "xar", 621 | "URL": "https://code.google.com/p/xar/wiki/xarformat", 622 | "Description": "Xar (archiver)eXtensible ARchive format weburl", 623 | "Popular": 0 624 | }, 625 | { 626 | "Hexadecimal File Signature": "504D4F43434D4F43", 627 | "ISO 8859-1": "PMOCCMOC", 628 | "Filename Extension": "dat", 629 | "URL": "http://www.howtogeek.com/79820/easily-restore-your-computer-with-file-and-settings-transfer-wizard-xp-part-1/", 630 | "Description": "Windows Files And Settings Transfer Repository weburl", 631 | "Popular": 0 632 | }, 633 | { 634 | "Hexadecimal File Signature": "4E45531A", 635 | "ISO 8859-1": "NES", 636 | "Filename Extension": "nes", 637 | "URL": "http://sadistech.com/nesromtool/romdoc.html", 638 | "Description": "Nintendo Entertainment System ROM file weburl", 639 | "Popular": 0 640 | }, 641 | { 642 | "Hexadecimal File Signature": "75737461720030307573746172202000", 643 | "ISO 8859-1": "ustar.00 ustar .", 644 | "Filename Extension": "tar", 645 | "URL": "https://www.gnu.org/software/tar/manual/html_node/Standard.html", 646 | "Description": "tar (computing)tar archive weburl", 647 | "Popular": 0 648 | }, 649 | { 650 | "Hexadecimal File Signature": "4F4152??", 651 | "ISO 8859-1": "OAR.", 652 | "Filename Extension": "oar", 653 | "Description": "OAR file archive format, where ?? is the format version.", 654 | "Popular": 0 655 | }, 656 | { 657 | "Hexadecimal File Signature": "746F7833", 658 | "ISO 8859-1": "TOX", 659 | "Filename Extension": "tox", 660 | "URL": "http://tox.land/uvox/man.html", 661 | "Description": "Open source portable voxel file weburl", 662 | "Popular": 0 663 | }, 664 | { 665 | "Hexadecimal File Signature": "4D4C5649", 666 | "ISO 8859-1": "MLVI", 667 | "Filename Extension": "MLV", 668 | "URL": "https://docs.google.com/spreadsheet/ccc?key", 669 | "Description": "Magic Lantern (firmware)Magic Lantern Video file weburl", 670 | "Popular": 0 671 | }, 672 | { 673 | "Hexadecimal File Signature": "44434D0150413330", 674 | "ISO 8859-1": "DCM PA30", 675 | "Filename Extension": null, 676 | "URL": "1562 Binary Delta Compression]{{Cite web", 677 | "Description": "Windows Update [http://www.microsoft.com/en-us/download/details.aspx?id", 678 | "Popular": 0 679 | }, 680 | { 681 | "Hexadecimal File Signature": "377ABCAF271C", 682 | "ISO 8859-1": "7z\u00bc\u00af'", 683 | "Filename Extension": "7z", 684 | "Description": "7-Zip File Format", 685 | "Popular": 0 686 | }, 687 | { 688 | "Hexadecimal File Signature": "1F8B", 689 | "ISO 8859-1": null, 690 | "Filename Extension": "gztar.gz", 691 | "URL": "https://tools.ietf.org/html/rfc1952#page-6", 692 | "Description": "GZIP compressed fileCite weburl", 693 | "Popular": 0 694 | }, 695 | { 696 | "Hexadecimal File Signature": "FD377A585A00", 697 | "ISO 8859-1": "\u00b27zXZ..", 698 | "Filename Extension": "xztar.xz", 699 | "Description": "XZ UtilsXZ compression utility using Lempel\u2013Ziv\u2013Markov chain algorithmLZMA2 compression", 700 | "Popular": 0 701 | }, 702 | { 703 | "Hexadecimal File Signature": "04224D18", 704 | "ISO 8859-1": ".\"M.", 705 | "Filename Extension": "lz4", 706 | "URL": "https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md", 707 | "Description": "LZ4 (compression algorithm)LZ4 Frame Format weburl", 708 | "Popular": 0 709 | }, 710 | { 711 | "Hexadecimal File Signature": "4D534346", 712 | "ISO 8859-1": "MSCF", 713 | "Filename Extension": "cab", 714 | "Description": "Microsoft Cabinet file", 715 | "Popular": 0 716 | }, 717 | { 718 | "Hexadecimal File Signature": "535A444488F02733", 719 | "ISO 8859-1": "SZDD....", 720 | "Filename Extension": "Various. (Replacing the last character of the original filename extension with an underscore, e.g. setup.exe becomes setup.ex_)", 721 | "Description": "Microsoft compressed file in Quantum compressionQuantum format, used prior to Windows XP. File can be decompressed using Extract.exe or Expand.exe distributed with earlier versions of Windows.", 722 | "Popular": 0 723 | }, 724 | { 725 | "Hexadecimal File Signature": "464C4946", 726 | "ISO 8859-1": "FLIF", 727 | "Filename Extension": "flif", 728 | "Description": "Free Lossless Image Format", 729 | "Popular": 0 730 | }, 731 | { 732 | "Hexadecimal File Signature": "1A45DFA3", 733 | "ISO 8859-1": ".E\u00df\u00a3", 734 | "Filename Extension": "mkvmkamksmk3dwebm", 735 | "Description": "Matroska media container, including WebM", 736 | "Popular": 0 737 | }, 738 | { 739 | "Hexadecimal File Signature": "4D494C20", 740 | "ISO 8859-1": "MIL", 741 | "Filename Extension": "stg", 742 | "Description": "\"SEAN : Session Analysis\" Training file. Also used in compatible software \"Rpw : Rowperfect for Windows\" and \"RP3W : ROWPERFECT3 for Windows\".", 743 | "Popular": 0 744 | }, 745 | { 746 | "Hexadecimal File Signature": "41542654464F524D????????444A56", 747 | "ISO 8859-1": "AT&TFORM....DJV", 748 | "Filename Extension": "djvudjv", 749 | "Description": "DjVu documentThe following byte is either 55 (U) for single-page or 4D (M) for multi-page documents.", 750 | "Popular": 0 751 | }, 752 | { 753 | "Hexadecimal File Signature": "3082", 754 | "ISO 8859-1": null, 755 | "Filename Extension": "der", 756 | "Description": "DER encoded X.509 certificate", 757 | "Popular": 0 758 | }, 759 | { 760 | "Hexadecimal File Signature": "4449434D", 761 | "ISO 8859-1": "DICM", 762 | "Filename Extension": "dcm", 763 | "Description": "DICOMDICOM Medical File Format", 764 | "Popular": 0 765 | }, 766 | { 767 | "Hexadecimal File Signature": "774F4646", 768 | "ISO 8859-1": "wOFF", 769 | "Filename Extension": "woff", 770 | "Description": "[https://www.w3.org/TR/2012/REC-WOFF-20121213/ WOFF File Format 1.0]", 771 | "Popular": 0 772 | }, 773 | { 774 | "Hexadecimal File Signature": "774F4632", 775 | "ISO 8859-1": "wOF2", 776 | "Filename Extension": "woff2", 777 | "Description": "[https://www.w3.org/TR/WOFF2/ WOFF File Format 2.0]", 778 | "Popular": 0 779 | }, 780 | { 781 | "Hexadecimal File Signature": "3c3f786d6c20", 782 | "ISO 8859-1": "?xml", 783 | "Filename Extension": "XML", 784 | "Description": "XMLeXtensible Markup Language when using the ASCII character encoding", 785 | "Popular": 0 786 | }, 787 | { 788 | "Hexadecimal File Signature": "0061736d", 789 | "ISO 8859-1": ".asm", 790 | "Filename Extension": "wasm", 791 | "URL": "https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#high-level-structure", 792 | "Description": "WebAssembly binary formatCite weburl", 793 | "Popular": 0 794 | }, 795 | { 796 | "Hexadecimal File Signature": "cf8401", 797 | "ISO 8859-1": null, 798 | "Filename Extension": "lep", 799 | "URL": "https://blogs.dropbox.com/tech/2016/07/lepton-image-compression-saving-22-losslessly-from-images-at-15mbs/", 800 | "Description": "Lepton image compression formatLepton compressed JPEG imageCite weburl", 801 | "Popular": 0 802 | }, 803 | { 804 | "Hexadecimal File Signature": "435753465753", 805 | "ISO 8859-1": "CWS FWS", 806 | "Filename Extension": "swf", 807 | "Description": "flash .swf", 808 | "Popular": 0 809 | }, 810 | { 811 | "Hexadecimal File Signature": "213C617263683E", 812 | "ISO 8859-1": null, 813 | "Filename Extension": "deb", 814 | "Description": "linux deb file", 815 | "Popular": 0 816 | }, 817 | { 818 | "Hexadecimal File Signature": "52494646????????57454250", 819 | "ISO 8859-1": "RIFF.... WEBP", 820 | "Filename Extension": "webp", 821 | "Description": "Google WebP image file, where ?? ?? ?? ?? is the file size. More information on [https://developers.google.com/speed/webp/docs/riff_container#webp_file_header WebP File Header]", 822 | "Popular": 0 823 | }, 824 | { 825 | "Hexadecimal File Signature": "27051956", 826 | "ISO 8859-1": "'..V", 827 | "Filename Extension": null, 828 | "URL": "http://buffalo.nas-central.org/wiki/How_to_Extract_an_uImage", 829 | "Description": "U-Boot / uImage. Das U-Boot Universal Boot Loader.Cite weburl", 830 | "Popular": 0 831 | }, 832 | { 833 | "Hexadecimal File Signature": "7B5C72746631", 834 | "ISO 8859-1": "\\rtf1", 835 | "Filename Extension": "rtf", 836 | "Description": "Rich Text Format", 837 | "Popular": 0 838 | }, 839 | { 840 | "Hexadecimal File Signature": "54415045", 841 | "ISO 8859-1": "TAPE", 842 | "Filename Extension": null, 843 | "Description": "Microsoft Tape Format", 844 | "Popular": 0 845 | }, 846 | { 847 | "Hexadecimal File Signature": "47", 848 | "ISO 8859-1": null, 849 | "Filename Extension": "ts tsv tsa", 850 | "Description": "MPEG Transport Stream (MPEG-2 Part 1)", 851 | "Popular": 0 852 | }, 853 | { 854 | "Hexadecimal File Signature": "000001BA", 855 | "ISO 8859-1": null, 856 | "Filename Extension": "m2p vob", 857 | "Description": "MPEG Program Stream (MPEG-1 Part 1 (essentially identical) and MPEG-2 Part 1)", 858 | "Popular": 0 859 | }, 860 | { 861 | "Hexadecimal File Signature": "000001BA47000001B3", 862 | "ISO 8859-1": null, 863 | "Filename Extension": "mpg mpeg", 864 | "Description": "MPEG Program Stream MPEG Transport Stream MPEG-1 video and MPEG-2 video (MPEG-1 Part 2 and MPEG-2 Part 2)", 865 | "Popular": 0 866 | }, 867 | { 868 | "Hexadecimal File Signature": "000000186674797069736F6D", 869 | "ISO 8859-1": "ftypisom", 870 | "Filename Extension": "mp4", 871 | "Description": "ISO Base Media file (MPEG-4)", 872 | "Popular": 0 873 | }, 874 | { 875 | "Hexadecimal File Signature": "7801785E789C78DA7820787D78BB78F9", 876 | "ISO 8859-1": null, 877 | "Filename Extension": "zlib", 878 | "Description": "No Compression (no preset dictionary) Best speed (no preset dictionary) Default Compression (no preset dictionary) Best Compression (no preset dictionary) No Compression (with preset dictionary) Best speed (with preset dictionary) Default Compression (with preset dictionary) Best Compression (with preset dictionary)", 879 | "Popular": 0 880 | }, 881 | { 882 | "Hexadecimal File Signature": "62767832", 883 | "ISO 8859-1": "bvx2", 884 | "Filename Extension": "lzfse", 885 | "URL": "https://github.com/lzfse/lzfse", 886 | "Description": "LZFSE - Lempel-Ziv style data compression algorithm using Finite State Entropy coding. OSS by Apple.Cite weburl", 887 | "Popular": 0 888 | }, 889 | { 890 | "Hexadecimal File Signature": "4F5243", 891 | "ISO 8859-1": "ORC", 892 | "Filename Extension": "orc", 893 | "Description": "Apache ORC (Optimized Row Columnar) file format", 894 | "Popular": 0 895 | }, 896 | { 897 | "Hexadecimal File Signature": "4F626A01", 898 | "ISO 8859-1": "Obj.", 899 | "Filename Extension": "avro", 900 | "Description": "Apache Avro binary file format", 901 | "Popular": 0 902 | }, 903 | { 904 | "Hexadecimal File Signature": "53455136", 905 | "ISO 8859-1": "SEQ6", 906 | "Filename Extension": "rc", 907 | "Description": "RCFile columnar file format", 908 | "Popular": 0 909 | }, 910 | { 911 | "Hexadecimal File Signature": "65877856", 912 | "ISO 8859-1": "e.xV", 913 | "Filename Extension": "p25 obt", 914 | "Description": "PhotoCap Object Templates", 915 | "Popular": 0 916 | }, 917 | { 918 | "Hexadecimal File Signature": "5555aaaa", 919 | "ISO 8859-1": null, 920 | "Filename Extension": "pcv", 921 | "Description": "PhotoCap Vector", 922 | "Popular": 0 923 | }, 924 | { 925 | "Hexadecimal File Signature": "785634", 926 | "ISO 8859-1": "xV4", 927 | "Filename Extension": "pbt pdt pea peb pet pgt pict pjt pkt pmt", 928 | "Description": "PhotoCap Template", 929 | "Popular": 0 930 | }, 931 | { 932 | "Hexadecimal File Signature": "50415231", 933 | "ISO 8859-1": "PAR1", 934 | "Filename Extension": null, 935 | "Description": "Apache Parquet columnar file format", 936 | "Popular": 0 937 | }, 938 | { 939 | "Hexadecimal File Signature": "454D5832", 940 | "ISO 8859-1": "EMX2", 941 | "Filename Extension": "ez2", 942 | "Description": "E-mu_EmaxEmulator Emaxsynth samples", 943 | "Popular": 0 944 | }, 945 | { 946 | "Hexadecimal File Signature": "454D5533", 947 | "ISO 8859-1": "EMU3", 948 | "Filename Extension": "ez3 iso", 949 | "Description": "E-mu_Emulator#The_Emulator_IIIEmulator III synth samples", 950 | "Popular": 0 951 | }, 952 | { 953 | "Hexadecimal File Signature": "1B4C7561", 954 | "ISO 8859-1": ".Lua", 955 | "Filename Extension": "luac", 956 | "URL": "Lua 5.2 Bytecode and Virtual Machine ", 957 | "Description": "Lua (programming language)Lua bytecode web title", 958 | "Popular": 0 959 | }, 960 | { 961 | "Hexadecimal File Signature": "626F6F6B000000006D61726B00000000", 962 | "ISO 8859-1": "62 6F 6F 6B 00 00 00 00 6D 61 72 6B 00 00 00 00", 963 | "Filename Extension": "alias", 964 | "URL": "https://www.forensicswiki.org/wiki/Mac_OS_X#Burn_Folder", 965 | "Description": "macOS file AliasCite weburl", 966 | "Popular": 0 967 | }, 968 | { 969 | "Hexadecimal File Signature": "5B5A6F6E655472616E736665725D", 970 | "ISO 8859-1": "[ZoneTransfer]", 971 | "Filename Extension": "Identifier", 972 | "URL": "vs.60) URL Security Zones]{{Cite web", 973 | "Description": "Microsoft Zone Identifier for [https://technet.microsoft.com/en-us/windows/ms537183(v", 974 | "Popular": 0 975 | }, 976 | { 977 | "Hexadecimal File Signature": "5265636569766564", 978 | "ISO 8859-1": "Received:", 979 | "Filename Extension": "eml", 980 | "URL": "http://file-extension.net/seeker/program_extension_message", 981 | "Description": "Email Message var5Cite weburl", 982 | "Popular": 0 983 | }, 984 | { 985 | "Hexadecimal File Signature": "20020162A01EAB0702000000", 986 | "ISO 8859-1": "20 02 01 62 A0 1E AB 07 02 00 00 00", 987 | "Filename Extension": "tde", 988 | "Description": "Tableau Datasource", 989 | "Popular": 0 990 | }, 991 | { 992 | "Hexadecimal File Signature": "3748030200000000583530394B4559", 993 | "ISO 8859-1": "37 48 03 02 00 00 00 00 58 35 30 39 4B 45 59", 994 | "Filename Extension": "kdb", 995 | "Description": "KDB file", 996 | "Popular": 0 997 | }, 998 | { 999 | "Hexadecimal File Signature": "85????03", 1000 | "ISO 8859-1": "85 ?? ?? 03", 1001 | "Filename Extension": "pgp", 1002 | "URL": "https://security.stackexchange.com/a/144555/12109", 1003 | "Description": "PGP file Cite weburl", 1004 | "Popular": 0 1005 | }, 1006 | { 1007 | "Hexadecimal File Signature": "28B52FFD", 1008 | "ISO 8859-1": "28 B5 2F FD", 1009 | "Filename Extension": "zst", 1010 | "URL": "https://tools.ietf.org/html/rfc8478#section-3.1.1", 1011 | "Description": "Zstandard compressed fileCite weburl", 1012 | "Popular": 0 1013 | }, 1014 | { 1015 | "Hexadecimal File Signature": "5253564B44415441", 1016 | "ISO 8859-1": "52 53 56 4B 44 41 54 41", 1017 | "Filename Extension": "rs", 1018 | "URL": "https://filext.com/file-extension/RS", 1019 | "Description": "QuickZip rs compressed archiveCite weburl", 1020 | "Popular": 0 1021 | }, 1022 | { 1023 | "Hexadecimal File Signature": "3A290A", 1024 | "ISO 8859-1": "3A 29 0A", 1025 | "Filename Extension": "sml", 1026 | "Description": "Smile (data interchange format)Smile file", 1027 | "Popular": 0 1028 | }, 1029 | { 1030 | "Hexadecimal File Signature": "4A6F7921", 1031 | "ISO 8859-1": "4A 6F 79 21", 1032 | "Filename Extension": null, 1033 | "Description": "Preferred Executable Format", 1034 | "Popular": 0 1035 | }, 1036 | { 1037 | "Hexadecimal File Signature": "310A3030", 1038 | "ISO 8859-1": "31 0A 30 30", 1039 | "Filename Extension": "srt", 1040 | "Description": "SubRip File", 1041 | "Popular": 0 1042 | }, 1043 | { 1044 | "Hexadecimal File Signature": "3412AA55", 1045 | "ISO 8859-1": "34 12 AA 55", 1046 | "Filename Extension": "vpk", 1047 | "Description": "VPK file, used to store game data for some Source Engine games", 1048 | "Popular": 0 1049 | }, 1050 | { 1051 | "Hexadecimal File Signature": "58464952", 1052 | "ISO 8859-1": "58 46 49 52", 1053 | "Filename Extension": "dcr", 1054 | "Description": "Adobe Shockwave", 1055 | "Popular": 0 1056 | }, 1057 | { 1058 | "Hexadecimal File Signature": "2A2A4143452A3A", 1059 | "ISO 8859-1": "2A 2A 41 43 45 2A 2A", 1060 | "Filename Extension": "ace", 1061 | "Description": "ACE (compressed file format)", 1062 | "Popular": 0 1063 | }, 1064 | { 1065 | "Hexadecimal File Signature": "60EA", 1066 | "ISO 8859-1": "60 RA", 1067 | "Filename Extension": "arj", 1068 | "Description": "ARJ", 1069 | "Popular": 0 1070 | }, 1071 | { 1072 | "Hexadecimal File Signature": "49536328", 1073 | "ISO 8859-1": "49 53 63 28", 1074 | "Filename Extension": "cab", 1075 | "Description": "InstallShield CAB Archive File", 1076 | "Popular": 0 1077 | }, 1078 | { 1079 | "Hexadecimal File Signature": "4B57414A", 1080 | "ISO 8859-1": "4B 57 41 4A", 1081 | "Filename Extension": "??_", 1082 | "Description": "Windows 3.1x Compressed File", 1083 | "Popular": 0 1084 | }, 1085 | { 1086 | "Hexadecimal File Signature": "535A4444", 1087 | "ISO 8859-1": "53 5A 44 44", 1088 | "Filename Extension": "??_", 1089 | "Description": "Windows 9x Compressed File", 1090 | "Popular": 0 1091 | }, 1092 | { 1093 | "Hexadecimal File Signature": "5A4F4F", 1094 | "ISO 8859-1": "5A 4F 4F", 1095 | "Filename Extension": "zoo", 1096 | "Description": "Zoo (file format)", 1097 | "Popular": 0 1098 | }, 1099 | { 1100 | "Hexadecimal File Signature": "50310A", 1101 | "ISO 8859-1": "50 31 0A", 1102 | "Filename Extension": "pbm", 1103 | "Description": "Portable bitmap", 1104 | "Popular": 0 1105 | }, 1106 | { 1107 | "Hexadecimal File Signature": "50320A", 1108 | "ISO 8859-1": "50 32 0A", 1109 | "Filename Extension": "pgm", 1110 | "Description": "Portable Gray Map", 1111 | "Popular": 0 1112 | }, 1113 | { 1114 | "Hexadecimal File Signature": "50330A", 1115 | "ISO 8859-1": "50 33 0A", 1116 | "Filename Extension": "ppm", 1117 | "Description": "Portable Pixmap", 1118 | "Popular": 0 1119 | }, 1120 | { 1121 | "Hexadecimal File Signature": "D7CDC69A", 1122 | "ISO 8859-1": "D7 CD C6 9A", 1123 | "Filename Extension": "wmf", 1124 | "Description": "Windows Metafile", 1125 | "Popular": 0 1126 | }, 1127 | { 1128 | "Hexadecimal File Signature": "67696D7020786366", 1129 | "ISO 8859-1": "67 69 6D 70 20 78 63 66", 1130 | "Filename Extension": "xcf", 1131 | "Description": "XCF (file format)", 1132 | "Popular": 0 1133 | }, 1134 | { 1135 | "Hexadecimal File Signature": "2F2A2058504D202A2F", 1136 | "ISO 8859-1": "2F 2A 20 58 50 4D 20 2A 2F", 1137 | "Filename Extension": "xpm", 1138 | "Description": "X PixMap", 1139 | "Popular": 0 1140 | }, 1141 | { 1142 | "Hexadecimal File Signature": "414646", 1143 | "ISO 8859-1": "41 46 46", 1144 | "Filename Extension": "aff", 1145 | "Description": "Advanced Forensics Format", 1146 | "Popular": 0 1147 | }, 1148 | { 1149 | "Hexadecimal File Signature": "45564632", 1150 | "ISO 8859-1": "45 56 46 32", 1151 | "Filename Extension": "Ex01", 1152 | "Description": "EnCase EWF version 2 format", 1153 | "Popular": 0 1154 | }, 1155 | { 1156 | "Hexadecimal File Signature": "455646", 1157 | "ISO 8859-1": "45 56 46", 1158 | "Filename Extension": "e01", 1159 | "Description": "EnCase EWF version 1 format", 1160 | "Popular": 0 1161 | }, 1162 | { 1163 | "Hexadecimal File Signature": "514649", 1164 | "ISO 8859-1": "51 46 49", 1165 | "Filename Extension": "qcow", 1166 | "Description": "qcow file format", 1167 | "Popular": 0 1168 | }, 1169 | { 1170 | "Hexadecimal File Signature": "52494646????????41434F4E", 1171 | "ISO 8859-1": "RIFF.... ACON", 1172 | "Filename Extension": "ani", 1173 | "Description": "Animated cursor", 1174 | "Popular": 0 1175 | }, 1176 | { 1177 | "Hexadecimal File Signature": "52494646????????43444441", 1178 | "ISO 8859-1": "RIFF.... CDDA", 1179 | "Filename Extension": "cda", 1180 | "Description": "Compact Disc Digital Audio", 1181 | "Popular": 0 1182 | }, 1183 | { 1184 | "Hexadecimal File Signature": "52494646????????514C434D", 1185 | "ISO 8859-1": "RIFF.... QLCM", 1186 | "Filename Extension": "qcp", 1187 | "Description": "Qualcomm PureVoice file format", 1188 | "Popular": 0 1189 | }, 1190 | { 1191 | "Hexadecimal File Signature": "58464952????????3339564D", 1192 | "ISO 8859-1": "XFIR.... 39VM", 1193 | "Filename Extension": "dir", 1194 | "Description": "Macromedia Director file format", 1195 | "Popular": 0 1196 | }, 1197 | { 1198 | "Hexadecimal File Signature": "464C56", 1199 | "ISO 8859-1": "46 4C 56", 1200 | "Filename Extension": "flv", 1201 | "Description": "Flash Video file", 1202 | "Popular": 0 1203 | }, 1204 | { 1205 | "Hexadecimal File Signature": "3C3C3C204F7261636C6520564D205669727475616C426F78204469736B20496D616765203E3E3E", 1206 | "ISO 8859-1": "3C 3C 3C 20 4F 72 61 63 6C 65 20 56 4D 20 56 69 72 74 75 61 6C 42 6F 78 20 44 69 73 6B 20 49 6D 61 67 65 20 3E 3E 3E", 1207 | "Filename Extension": "vdi", 1208 | "Description": "VirtualBox Virtual Hard Disk file format", 1209 | "Popular": 0 1210 | }, 1211 | { 1212 | "Hexadecimal File Signature": "636F6E6E6563746978", 1213 | "ISO 8859-1": "63 6F 6E 6E 65 63 74 69 78", 1214 | "Filename Extension": "vhd", 1215 | "Description": "Windows Virtual PC Virtual Hard Disk file format", 1216 | "Popular": 0 1217 | }, 1218 | { 1219 | "Hexadecimal File Signature": "7668647866696C65", 1220 | "ISO 8859-1": "76 68 64 78 66 69 6C 65", 1221 | "Filename Extension": "vhdx", 1222 | "Description": "Windows Virtual PC Windows 8 Virtual Hard Disk file format", 1223 | "Popular": 0 1224 | }, 1225 | { 1226 | "Hexadecimal File Signature": "49735A21", 1227 | "ISO 8859-1": "49 73 5A 21", 1228 | "Filename Extension": "isz", 1229 | "Description": "Compressed ISO image", 1230 | "Popular": 0 1231 | }, 1232 | { 1233 | "Hexadecimal File Signature": "444141", 1234 | "ISO 8859-1": "44 41 41", 1235 | "Filename Extension": "daa", 1236 | "Description": "Direct Access Archive PowerISO", 1237 | "Popular": 0 1238 | }, 1239 | { 1240 | "Hexadecimal File Signature": "4C664C65", 1241 | "ISO 8859-1": "4C 66 4C 65", 1242 | "Filename Extension": "evt", 1243 | "Description": "Windows Event Viewer file format", 1244 | "Popular": 0 1245 | }, 1246 | { 1247 | "Hexadecimal File Signature": "504D4343", 1248 | "ISO 8859-1": "50 4D 43 43", 1249 | "Filename Extension": "grp", 1250 | "Description": "Windows 3.x Program Manager Program Group file format", 1251 | "Popular": 0 1252 | }, 1253 | { 1254 | "Hexadecimal File Signature": "4B434D53", 1255 | "ISO 8859-1": "4B 43 4D 53", 1256 | "Filename Extension": "icm", 1257 | "Description": "ICC profile", 1258 | "Popular": 0 1259 | }, 1260 | { 1261 | "Hexadecimal File Signature": "72656766", 1262 | "ISO 8859-1": "72 65 67 66", 1263 | "Filename Extension": "dat", 1264 | "Description": "Windows Registry file", 1265 | "Popular": 0 1266 | }, 1267 | { 1268 | "Hexadecimal File Signature": "2142444E", 1269 | "ISO 8859-1": "21 42 44 4E", 1270 | "Filename Extension": "pst", 1271 | "Description": "Microsoft Outlook Personal Storage Table file", 1272 | "Popular": 0 1273 | }, 1274 | { 1275 | "Hexadecimal File Signature": "445241434F", 1276 | "ISO 8859-1": "44 52 41 43 4F", 1277 | "Filename Extension": "drc", 1278 | "URL": "", 1279 | "Description": "3D model compressed with Google DracoCite weblast", 1280 | "Popular": 0 1281 | }, 1282 | { 1283 | "Hexadecimal File Signature": "47524942", 1284 | "ISO 8859-1": "47 52 49 42", 1285 | "Filename Extension": "grib grib2", 1286 | "Description": "Gridded data (commonly weather observations or forecasts) in the World Meteorological OrganizationWMO GRIB or GRIB2 formathttps://www.wmo.int/pages/prog/www/WDM/Guides/Guide-binary-2.html", 1287 | "Popular": 0 1288 | }, 1289 | { 1290 | "Hexadecimal File Signature": "424C454E444552", 1291 | "ISO 8859-1": "BLENDER", 1292 | "Filename Extension": "blend", 1293 | "URL": "2015-04-18", 1294 | "Description": "Blender File FormatCite webdate", 1295 | "Popular": 0 1296 | } 1297 | ] -------------------------------------------------------------------------------- /pywhat/Data/phone_codes.json: -------------------------------------------------------------------------------- 1 | { 2 | "+93": "Afghanistan", 3 | "+358": "Finland", 4 | "+355": "Albania", 5 | "+213": "Algeria", 6 | "+1 684": "AmericanSamoa", 7 | "+376": "Andorra", 8 | "+244": "Angola", 9 | "+1 264": "Anguilla", 10 | "+672": "Norfolk Island", 11 | "+1268": "Antigua and Barbuda", 12 | "+54": "Argentina", 13 | "+374": "Armenia", 14 | "+297": "Aruba", 15 | "+61": "Cocos (Keeling) Islands", 16 | "+43": "Austria", 17 | "+994": "Azerbaijan", 18 | "+1 242": "Bahamas", 19 | "+973": "Bahrain", 20 | "+880": "Bangladesh", 21 | "+1 246": "Barbados", 22 | "+375": "Belarus", 23 | "+32": "Belgium", 24 | "+501": "Belize", 25 | "+229": "Benin", 26 | "+1 441": "Bermuda", 27 | "+975": "Bhutan", 28 | "+591": "Bolivia, Plurinational State of", 29 | "+387": "Bosnia and Herzegovina", 30 | "+267": "Botswana", 31 | "+55": "Brazil", 32 | "+246": "British Indian Ocean Territory", 33 | "+673": "Brunei Darussalam", 34 | "+359": "Bulgaria", 35 | "+226": "Burkina Faso", 36 | "+257": "Burundi", 37 | "+855": "Cambodia", 38 | "+237": "Cameroon", 39 | "+1": "United States", 40 | "+238": "Cape Verde", 41 | "+ 345": "Cayman Islands", 42 | "+236": "Central African Republic", 43 | "+235": "Chad", 44 | "+56": "Chile", 45 | "+86": "China", 46 | "+57": "Colombia", 47 | "+269": "Comoros", 48 | "+242": "Congo", 49 | "+243": "Congo, The Democratic Republic of the Congo", 50 | "+682": "Cook Islands", 51 | "+506": "Costa Rica", 52 | "+225": "Cote d'Ivoire", 53 | "+385": "Croatia", 54 | "+53": "Cuba", 55 | "+357": "Cyprus", 56 | "+420": "Czech Republic", 57 | "+45": "Denmark", 58 | "+253": "Djibouti", 59 | "+1 767": "Dominica", 60 | "+1 849": "Dominican Republic", 61 | "+593": "Ecuador", 62 | "+20": "Egypt", 63 | "+503": "El Salvador", 64 | "+240": "Equatorial Guinea", 65 | "+291": "Eritrea", 66 | "+372": "Estonia", 67 | "+251": "Ethiopia", 68 | "+500": "South Georgia and the South Sandwich Islands", 69 | "+298": "Faroe Islands", 70 | "+679": "Fiji", 71 | "+33": "France", 72 | "+594": "French Guiana", 73 | "+689": "French Polynesia", 74 | "+241": "Gabon", 75 | "+220": "Gambia", 76 | "+995": "Georgia", 77 | "+49": "Germany", 78 | "+233": "Ghana", 79 | "+350": "Gibraltar", 80 | "+30": "Greece", 81 | "+299": "Greenland", 82 | "+1 473": "Grenada", 83 | "+590": "Saint Martin", 84 | "+1 671": "Guam", 85 | "+502": "Guatemala", 86 | "+44": "United Kingdom", 87 | "+224": "Guinea", 88 | "+245": "Guinea-Bissau", 89 | "+595": "Paraguay", 90 | "+509": "Haiti", 91 | "+379": "Holy See (Vatican City State)", 92 | "+504": "Honduras", 93 | "+852": "Hong Kong", 94 | "+36": "Hungary", 95 | "+354": "Iceland", 96 | "+91": "India", 97 | "+62": "Indonesia", 98 | "+98": "Iran, Islamic Republic of Persian Gulf", 99 | "+964": "Iraq", 100 | "+353": "Ireland", 101 | "+972": "Israel", 102 | "+39": "Italy", 103 | "+1 876": "Jamaica", 104 | "+81": "Japan", 105 | "+962": "Jordan", 106 | "+7 7": "Kazakhstan", 107 | "+254": "Kenya", 108 | "+686": "Kiribati", 109 | "+850": "Korea, Democratic People's Republic of Korea", 110 | "+82": "Korea, Republic of South Korea", 111 | "+383": "Kosovo", 112 | "+965": "Kuwait", 113 | "+996": "Kyrgyzstan", 114 | "+856": "Laos", 115 | "+371": "Latvia", 116 | "+961": "Lebanon", 117 | "+266": "Lesotho", 118 | "+231": "Liberia", 119 | "+218": "Libyan Arab Jamahiriya", 120 | "+423": "Liechtenstein", 121 | "+370": "Lithuania", 122 | "+352": "Luxembourg", 123 | "+853": "Macao", 124 | "+389": "Macedonia", 125 | "+261": "Madagascar", 126 | "+265": "Malawi", 127 | "+60": "Malaysia", 128 | "+960": "Maldives", 129 | "+223": "Mali", 130 | "+356": "Malta", 131 | "+692": "Marshall Islands", 132 | "+596": "Martinique", 133 | "+222": "Mauritania", 134 | "+230": "Mauritius", 135 | "+262": "Reunion", 136 | "+52": "Mexico", 137 | "+691": "Micronesia, Federated States of Micronesia", 138 | "+373": "Moldova", 139 | "+377": "Monaco", 140 | "+976": "Mongolia", 141 | "+382": "Montenegro", 142 | "+1664": "Montserrat", 143 | "+212": "Morocco", 144 | "+258": "Mozambique", 145 | "+95": "Myanmar", 146 | "+264": "Namibia", 147 | "+674": "Nauru", 148 | "+977": "Nepal", 149 | "+31": "Netherlands", 150 | "+599": "Netherlands Antilles", 151 | "+687": "New Caledonia", 152 | "+64": "New Zealand", 153 | "+505": "Nicaragua", 154 | "+227": "Niger", 155 | "+234": "Nigeria", 156 | "+683": "Niue", 157 | "+1 670": "Northern Mariana Islands", 158 | "+47": "Svalbard and Jan Mayen", 159 | "+968": "Oman", 160 | "+92": "Pakistan", 161 | "+680": "Palau", 162 | "+970": "Palestinian Territory, Occupied", 163 | "+507": "Panama", 164 | "+675": "Papua New Guinea", 165 | "+51": "Peru", 166 | "+63": "Philippines", 167 | "+872": "Pitcairn", 168 | "+48": "Poland", 169 | "+351": "Portugal", 170 | "+1 939": "Puerto Rico", 171 | "+974": "Qatar", 172 | "+40": "Romania", 173 | "+7": "Russia", 174 | "+250": "Rwanda", 175 | "+290": "Saint Helena, Ascension and Tristan Da Cunha", 176 | "+1 869": "Saint Kitts and Nevis", 177 | "+1 758": "Saint Lucia", 178 | "+508": "Saint Pierre and Miquelon", 179 | "+1 784": "Saint Vincent and the Grenadines", 180 | "+685": "Samoa", 181 | "+378": "San Marino", 182 | "+239": "Sao Tome and Principe", 183 | "+966": "Saudi Arabia", 184 | "+221": "Senegal", 185 | "+381": "Serbia", 186 | "+248": "Seychelles", 187 | "+232": "Sierra Leone", 188 | "+65": "Singapore", 189 | "+421": "Slovakia", 190 | "+386": "Slovenia", 191 | "+677": "Solomon Islands", 192 | "+252": "Somalia", 193 | "+27": "South Africa", 194 | "+34": "Spain", 195 | "+94": "Sri Lanka", 196 | "+249": "Sudan", 197 | "+597": "SuriName", 198 | "+268": "Swaziland", 199 | "+46": "Sweden", 200 | "+41": "Switzerland", 201 | "+963": "Syrian Arab Republic", 202 | "+886": "Taiwan", 203 | "+992": "Tajikistan", 204 | "+255": "Tanzania, United Republic of Tanzania", 205 | "+66": "Thailand", 206 | "+670": "Timor-Leste", 207 | "+228": "Togo", 208 | "+690": "Tokelau", 209 | "+676": "Tonga", 210 | "+1 868": "Trinidad and Tobago", 211 | "+216": "Tunisia", 212 | "+90": "Turkey", 213 | "+993": "Turkmenistan", 214 | "+1 649": "Turks and Caicos Islands", 215 | "+688": "Tuvalu", 216 | "+256": "Uganda", 217 | "+380": "Ukraine", 218 | "+971": "United Arab Emirates", 219 | "+598": "Uruguay", 220 | "+998": "Uzbekistan", 221 | "+678": "Vanuatu", 222 | "+58": "Venezuela, Bolivarian Republic of Venezuela", 223 | "+84": "Vietnam", 224 | "+1 284": "Virgin Islands, British", 225 | "+1 340": "Virgin Islands, U.S.", 226 | "+681": "Wallis and Futuna", 227 | "+967": "Yemen", 228 | "+260": "Zambia", 229 | "+263": "Zimbabwe" 230 | } -------------------------------------------------------------------------------- /pywhat/__init__.py: -------------------------------------------------------------------------------- 1 | from pywhat.filter import Distribution, Filter 2 | from pywhat.helper import AvailableTags, Keys 3 | from pywhat.identifier import Identifier 4 | 5 | __version__ = "5.0.0" 6 | 7 | tags = AvailableTags().get_tags() 8 | pywhat_tags = tags # left for backward compatibility purposes 9 | 10 | __all__ = ["Identifier", "Distribution", "tags", "pywhat_tags", "Keys", "Filter"] 11 | 12 | 13 | del AvailableTags, filter 14 | 15 | 16 | def __dir__(): 17 | return __all__ + ["__version__"] 18 | -------------------------------------------------------------------------------- /pywhat/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | """ 4 | pyWhat: Identify Anything. 5 | """ 6 | 7 | import platform 8 | import sys 9 | 10 | if __name__ == "__main__": 11 | if sys.version_info < (3, 6): 12 | print( 13 | f"What requires Python 3.6+, you are using {platform.python_version()}. Please install a higher Python version." 14 | ) 15 | sys.exit(1) 16 | 17 | from pywhat import what 18 | 19 | if len(sys.argv) == 1: 20 | what.main(["--help"]) 21 | 22 | what.main() 23 | -------------------------------------------------------------------------------- /pywhat/filter.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Mapping 2 | from typing import Optional 3 | 4 | from pywhat.helper import AvailableTags, CaseInsensitiveSet, InvalidTag, load_regexes 5 | 6 | 7 | class Filter(Mapping): 8 | """ 9 | A filter is an object containing the filtration information. 10 | The difference from Distribution object is 11 | that Filter object does not store regexes. 12 | 13 | Example filters: 14 | * {"Tags": ["Networking"]} 15 | * {"Tags": ["Identifiers"], "ExcludeTags": ["Credentials"], "MinRarity": 0.6} 16 | """ 17 | 18 | def __init__(self, filters_dict=None): 19 | tags = CaseInsensitiveSet(AvailableTags().get_tags()) 20 | self._dict = {} 21 | if filters_dict is None: 22 | filters_dict = {} 23 | 24 | self._dict["Tags"] = CaseInsensitiveSet(filters_dict.setdefault("Tags", tags)) 25 | self._dict["ExcludeTags"] = CaseInsensitiveSet( 26 | filters_dict.setdefault("ExcludeTags", set()) 27 | ) 28 | # We have regex with 0 rarity which trip false positive alarms all the time 29 | self._dict["MinRarity"] = filters_dict.setdefault("MinRarity", 0.1) 30 | self._dict["MaxRarity"] = filters_dict.setdefault("MaxRarity", 1) 31 | if not self._dict["Tags"].issubset(tags) or not self._dict[ 32 | "ExcludeTags" 33 | ].issubset(tags): 34 | raise InvalidTag("Passed filter contains tags that are not used by 'what'") 35 | 36 | def get_filter(self): 37 | return dict(self._dict) 38 | 39 | def __repr__(self): 40 | return f"{self.__class__.__name__}({self._dict})" 41 | 42 | def __and__(self, other): 43 | if type(self) != type(other): 44 | return NotImplemented 45 | tags = self._dict["Tags"] & other._dict["Tags"] 46 | exclude_tags = self._dict["ExcludeTags"] & other._dict["ExcludeTags"] 47 | min_rarity = max(self._dict["MinRarity"], other._dict["MinRarity"]) 48 | max_rarity = min(self._dict["MaxRarity"], other._dict["MaxRarity"]) 49 | return self.__class__( 50 | { 51 | "Tags": tags, 52 | "ExcludeTags": exclude_tags, 53 | "MinRarity": min_rarity, 54 | "MaxRarity": max_rarity, 55 | } 56 | ) 57 | 58 | def __or__(self, other): 59 | if type(self) != type(other): 60 | return NotImplemented 61 | tags = self._dict["Tags"] | other._dict["Tags"] 62 | exclude_tags = self._dict["ExcludeTags"] | other._dict["ExcludeTags"] 63 | min_rarity = min(self._dict["MinRarity"], other._dict["MinRarity"]) 64 | max_rarity = max(self._dict["MaxRarity"], other._dict["MaxRarity"]) 65 | return self.__class__( 66 | { 67 | "Tags": tags, 68 | "ExcludeTags": exclude_tags, 69 | "MinRarity": min_rarity, 70 | "MaxRarity": max_rarity, 71 | } 72 | ) 73 | 74 | def __iand__(self, other): 75 | if type(self) != type(other): 76 | return NotImplemented 77 | return self & other 78 | 79 | def __ior__(self, other): 80 | if type(self) != type(other): 81 | return NotImplemented 82 | return self | other 83 | 84 | def __getitem__(self, key): 85 | return self._dict[key] 86 | 87 | def __iter__(self): 88 | return iter(self._dict) 89 | 90 | def __len__(self): 91 | return len(self._dict) 92 | 93 | def __contains__(self, item): 94 | return ( 95 | self["MinRarity"] <= item["Rarity"] <= self["MaxRarity"] 96 | and set(item["Tags"]) & self["Tags"] 97 | and not set(item["Tags"]) & self["ExcludeTags"] 98 | ) 99 | 100 | def setdefault(self, key, default=None): 101 | return self._dict.setdefault(key, default) 102 | 103 | 104 | class Distribution(Filter): 105 | """ 106 | A distribution is an object containing the regex 107 | But the regex has gone through a filter process. 108 | 109 | Example filters: 110 | * {"Tags": ["Networking"]} 111 | * {"Tags": ["Identifiers"], "ExcludeTags": ["Credentials"], "MinRarity": 0.6} 112 | """ 113 | 114 | def __init__(self, filter: Optional[Filter] = None): 115 | super().__init__(filter) 116 | self._filter() 117 | 118 | def _filter(self): 119 | self._regexes = load_regexes() 120 | temp_regexes = [regex for regex in self._regexes if regex in self] 121 | self._regexes = temp_regexes 122 | 123 | def get_regexes(self): 124 | return list(self._regexes) 125 | -------------------------------------------------------------------------------- /pywhat/helper.py: -------------------------------------------------------------------------------- 1 | """Helper utilities""" 2 | import collections.abc 3 | import os.path 4 | import re 5 | from enum import Enum, auto 6 | from functools import lru_cache 7 | 8 | try: 9 | import orjson as json 10 | except ImportError: 11 | import json # type: ignore 12 | 13 | 14 | class AvailableTags: 15 | def __init__(self): 16 | self.tags = set() 17 | regexes = load_regexes() 18 | for regex in regexes: 19 | self.tags.update(regex["Tags"]) 20 | 21 | def get_tags(self): 22 | return self.tags 23 | 24 | 25 | class InvalidTag(Exception): 26 | """ 27 | This exception should be raised when Distribution() gets a filter 28 | containing non-existent tags. 29 | """ 30 | 31 | pass 32 | 33 | 34 | @lru_cache() 35 | def read_json(path: str): 36 | fullpath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Data/" + path) 37 | with open(fullpath, "rb") as myfile: 38 | return json.loads(myfile.read()) 39 | 40 | 41 | @lru_cache() 42 | def load_regexes() -> list: 43 | regexes = read_json("regex.json") 44 | for regex in regexes: 45 | regex["Boundaryless Regex"] = re.sub( 46 | r"(? dict: 39 | if dist is None: 40 | dist = self.distribution 41 | if key is None: 42 | key = self._key 43 | if reverse is None: 44 | reverse = self._reverse 45 | if boundaryless is None: 46 | boundaryless = self.boundaryless 47 | 48 | identify_obj: dict = {"File Signatures": {}, "Regexes": {}} 49 | search = [] 50 | 51 | if not only_text and os.path.isdir(text): 52 | # if input is a directory, recursively search for all of the files 53 | for myfile in glob.iglob(text + "/**", recursive=True): 54 | if os.path.isfile(myfile): 55 | search.append(os.path.abspath(myfile)) 56 | else: 57 | search = [text] 58 | 59 | for string in search: 60 | if not only_text and os.path.isfile(string): 61 | if os.path.isdir(text): 62 | short_name = string.replace(os.path.abspath(text), "") 63 | else: 64 | short_name = os.path.basename(string) 65 | 66 | magic_numbers = pywhat.magic_numbers.get_magic_nums(string) 67 | with open(string, "r", encoding="utf-8", errors="ignore") as file: 68 | contents = [file.read()] 69 | 70 | if include_filenames: 71 | contents.append(os.path.basename(string)) 72 | 73 | regex = self._regex_id.check( 74 | contents, dist=dist, boundaryless=boundaryless 75 | ) 76 | 77 | if not magic_numbers: 78 | magic_numbers = pywhat.magic_numbers.check_magic_nums(string) 79 | 80 | if magic_numbers: 81 | identify_obj["File Signatures"][short_name] = magic_numbers 82 | else: 83 | short_name = "text" 84 | regex = self._regex_id.check( 85 | search, dist=dist, boundaryless=boundaryless 86 | ) 87 | 88 | if regex: 89 | identify_obj["Regexes"][short_name] = regex 90 | 91 | for key_, value in identify_obj.items(): 92 | # if there are zero regex or file signature matches, set it to None 93 | if not value: 94 | identify_obj[key_] = None 95 | 96 | if key != Keys.NONE: 97 | identify_obj["Regexes"][short_name] = sorted( 98 | identify_obj["Regexes"][short_name], key=key, reverse=reverse 99 | ) 100 | 101 | return identify_obj 102 | -------------------------------------------------------------------------------- /pywhat/magic_numbers.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | 3 | from pywhat.helper import read_json 4 | 5 | 6 | def get_magic_nums(file_loc): 7 | with open(file_loc, "rb") as myfile: 8 | header = myfile.read(24) 9 | header = str(binascii.hexlify(header))[2:-1] 10 | return check_magic_nums(header) 11 | 12 | 13 | def check_magic_nums(text): 14 | for i in read_json("file_signatures.json"): 15 | to_check = i["Hexadecimal File Signature"] 16 | if text.lower().startswith(to_check.lower()): 17 | # A file can only be one type 18 | return i 19 | return None 20 | -------------------------------------------------------------------------------- /pywhat/printer.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | 5 | from rich.console import Console 6 | from rich.table import Table 7 | 8 | 9 | class Printing: 10 | def __init__(self): 11 | self.console = Console(highlight=False) 12 | self.bug_bounty_mode = False 13 | 14 | def pretty_print(self, text: dict, text_input, print_tags=False): 15 | to_out = "" 16 | 17 | if text["File Signatures"]: 18 | for key, value in text["File Signatures"].items(): 19 | if value: 20 | to_out += "\n" 21 | to_out += f"[bold #D7Afff]File Identified[/bold #D7Afff]: [bold]{key}[/bold] with Magic Numbers {value['ISO 8859-1']}." 22 | to_out += f"\n[bold #D7Afff]File Description: [/bold #D7Afff] {value['Description']}." 23 | to_out += "\n\n" 24 | 25 | if text["Regexes"]: 26 | to_out += "\n[bold #D7Afff]Possible Identification[/bold #D7Afff]\n" 27 | table = Table( 28 | show_header=True, header_style="bold #D7Afff", show_lines=True 29 | ) 30 | table.add_column("Matched Text", overflow="fold") 31 | table.add_column("Identified as", overflow="fold") 32 | table.add_column("Description", overflow="fold") 33 | 34 | if self._check_if_directory(text_input): 35 | # if input is a folder, add a filename column 36 | table.add_column("File", overflow="fold") 37 | 38 | # Check if there are any bug bounties with exploits 39 | # in the regex 40 | self._check_if_exploit_in_json(text) 41 | if self.bug_bounty_mode: 42 | table.add_column("Exploit", overflow="fold") 43 | 44 | for key, value in text["Regexes"].items(): 45 | for i in value: 46 | matched = i["Matched"] 47 | name = i["Regex Pattern"]["Name"] 48 | description = None 49 | filename = key 50 | exploit = None 51 | 52 | if "URL" in i["Regex Pattern"] and i["Regex Pattern"]["URL"]: 53 | description = ( 54 | "Click here to analyse in the browser\n" 55 | + i["Regex Pattern"]["URL"] 56 | + matched.replace(" ", "") 57 | ) 58 | 59 | if i["Regex Pattern"]["Description"]: 60 | if description: 61 | description = ( 62 | description + "\n" + i["Regex Pattern"]["Description"] 63 | ) 64 | else: 65 | description = i["Regex Pattern"]["Description"] 66 | 67 | if ( 68 | "Exploit" in i["Regex Pattern"] 69 | and i["Regex Pattern"]["Exploit"] 70 | ): 71 | exploit = i["Regex Pattern"]["Exploit"] 72 | 73 | if print_tags: 74 | tags = f"Tags: {', '.join(i['Regex Pattern']['Tags'])}" 75 | if description is None: 76 | description = tags 77 | else: 78 | description += "\n" + tags 79 | 80 | if description is None: 81 | description = "None" 82 | 83 | # FIXME this is quite messy 84 | if self.bug_bounty_mode: 85 | if self._check_if_directory(text_input): 86 | table.add_row( 87 | matched, 88 | name, 89 | description, 90 | filename, 91 | exploit, 92 | ) 93 | else: 94 | table.add_row( 95 | matched, 96 | name, 97 | description, 98 | exploit, 99 | ) 100 | elif self._check_if_directory(text_input): 101 | table.add_row( 102 | matched, 103 | name, 104 | description, 105 | filename, 106 | ) 107 | else: 108 | table.add_row( 109 | matched, 110 | name, 111 | description, 112 | ) 113 | 114 | self.console.print(to_out.strip(), table) 115 | 116 | elif not self.bug_bounty_mode: 117 | self.console.print((to_out + "\nNothing found!").lstrip()) 118 | 119 | def print_json(self, text: dict): 120 | print(json.dumps(text)) 121 | 122 | """ 123 | Does not create a table, prints it as raw text 124 | Returns the printable object 125 | """ 126 | 127 | def print_raw(self, text: dict, text_input, print_tags=False): 128 | output_str = "" 129 | 130 | if text["File Signatures"] and text["Regexes"]: 131 | for key, value in text["File Signatures"].items(): 132 | if value: 133 | output_str += "\n" 134 | output_str += f"[bold #D7Afff]File Identified[/bold #D7Afff]: [bold]{key}[/bold] with Magic Numbers {value['ISO 8859-1']}." 135 | output_str += f"\n[bold #D7Afff]File Description:[/bold #D7Afff] {value['Description']}." 136 | output_str += "\n" 137 | 138 | if text["Regexes"]: 139 | for key, value in text["Regexes"].items(): 140 | for i in value: 141 | description = None 142 | matched = i["Matched"] 143 | if self._check_if_directory(text_input): 144 | output_str += f"[bold #D7Afff]File: {key}[/bold #D7Afff]\n" 145 | output_str += ( 146 | "[bold #D7Afff]Matched on: [/bold #D7Afff]" + i["Matched"] 147 | ) 148 | output_str += ( 149 | "\n[bold #D7Afff]Name: [/bold #D7Afff]" 150 | + i["Regex Pattern"]["Name"] 151 | ) 152 | 153 | link = None 154 | if "URL" in i["Regex Pattern"] and i["Regex Pattern"]["URL"]: 155 | link = ( 156 | "\n[bold #D7Afff]Link: [/bold #D7Afff] " 157 | + i["Regex Pattern"]["URL"] 158 | + matched.replace(" ", "") 159 | ) 160 | 161 | if link: 162 | output_str += link 163 | 164 | if i["Regex Pattern"]["Description"]: 165 | description = ( 166 | "\n[bold #D7Afff]Description: [/bold #D7Afff]" 167 | + i["Regex Pattern"]["Description"] 168 | ) 169 | 170 | if description: 171 | output_str += description 172 | 173 | if ( 174 | "Exploit" in i["Regex Pattern"] 175 | and i["Regex Pattern"]["Exploit"] 176 | ): 177 | output_str += ( 178 | "\n[bold #D7Afff]Exploit: [/bold #D7Afff]" 179 | + i["Regex Pattern"]["Exploit"] 180 | ) 181 | 182 | if print_tags: 183 | output_str += f"\n[bold #D7Afff]Tags: [/bold #D7Afff]{', '.join(i['Regex Pattern']['Tags'])}" 184 | 185 | output_str += "\n\n" 186 | 187 | if output_str == "" and not self.bug_bounty_mode: 188 | self.console.print("Nothing found!") 189 | 190 | if output_str.strip(): 191 | self.console.print(output_str.rstrip()) 192 | 193 | return output_str 194 | 195 | def format_print(self, text: dict, format_str: str): 196 | if text["Regexes"]: 197 | output = [] 198 | format_list = [] 199 | 200 | # Split format_str so that format_list's item will either be r'\\' or something else 201 | start = 0 202 | i = format_str.find(r"\\", start) 203 | while i != -1: 204 | if format_str[start:i]: 205 | format_list.append(format_str[start:i]) 206 | format_list.append("\\") 207 | start = i + 2 208 | i = format_str.find(r"\\", start) 209 | format_list.append(format_str[start:]) 210 | 211 | for key, value in text["Regexes"].items(): 212 | for match in value: 213 | temp = "" 214 | for s in format_list: 215 | formats = { 216 | "%m": match["Matched"], 217 | "%n": match["Regex Pattern"]["Name"], 218 | "%d": match["Regex Pattern"]["Description"], 219 | "%e": match["Regex Pattern"].get("Exploit"), 220 | "%r": str(match["Regex Pattern"]["Rarity"]), 221 | "%l": match["Regex Pattern"]["URL"] + match["Matched"] 222 | if match["Regex Pattern"]["URL"] is not None 223 | else None, 224 | "%t": ", ".join(match["Regex Pattern"]["Tags"]), 225 | } 226 | for format, value in formats.items(): 227 | value = str() if value is None else value 228 | s = re.sub(r"(? bool: 238 | if "File Signatures" in text and text["File Signatures"]: 239 | # loops files 240 | for file in text["Regexes"].keys(): 241 | for i in text["Regexes"][file]: 242 | if "Exploit" in i.keys(): 243 | self.bug_bounty_mode = True 244 | else: 245 | for value in text["Regexes"]["text"]: 246 | if "Exploit" in value["Regex Pattern"].keys(): 247 | self.bug_bounty_mode = True 248 | 249 | return self.bug_bounty_mode 250 | 251 | def _check_if_directory(self, text_input): 252 | return os.path.isdir(text_input) 253 | -------------------------------------------------------------------------------- /pywhat/regex_identifier.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Optional 3 | 4 | from pywhat.filter import Distribution, Filter 5 | 6 | 7 | class RegexIdentifier: 8 | def __init__(self): 9 | self.distribution = Distribution() 10 | 11 | def check( 12 | self, 13 | text, 14 | dist: Optional[Distribution] = None, 15 | *, 16 | boundaryless: Optional[Filter] = None 17 | ): 18 | if dist is None: 19 | dist = self.distribution 20 | if boundaryless is None: 21 | boundaryless = Filter({"Tags": []}) 22 | matches = [] 23 | 24 | for string in text: 25 | for reg in dist.get_regexes(): 26 | regex = ( 27 | reg["Boundaryless Regex"] if reg in boundaryless else reg["Regex"] 28 | ) 29 | for matched_regex in re.finditer(regex, string, re.MULTILINE): 30 | reg_match = dict(reg) 31 | matched = self.clean_text(matched_regex.group(0)) 32 | 33 | if ( 34 | reg_match.get("Exploit") is not None 35 | and "curl" in reg_match["Exploit"] 36 | ): 37 | # Replace anything like XXXXX_XXXXXX_HERE with the match 38 | reg_match["Exploit"] = re.sub( 39 | r"[A-Z_]+_HERE", matched, reg_match["Exploit"] 40 | ) 41 | 42 | children = reg_match.get("Children") 43 | if children is not None: 44 | processed_match = re.sub( 45 | children.get("deletion_pattern", ""), "", matched 46 | ) 47 | matched_children = [] 48 | if children["method"] == "hashmap": 49 | for length in children["lengths"]: 50 | try: 51 | matched_children.append( 52 | children["Items"][processed_match[:length]] 53 | ) 54 | except KeyError: 55 | continue 56 | else: 57 | for element in children["Items"]: 58 | if ( 59 | children["method"] == "regex" 60 | and re.search( 61 | element, processed_match, re.MULTILINE 62 | ) 63 | ) or ( 64 | children["method"] == "startswith" 65 | and processed_match.startswith(element) 66 | ): 67 | matched_children.append(children["Items"][element]) 68 | 69 | if matched_children: 70 | reg_match["Description"] = children.get( 71 | "entry", "" 72 | ) + ", ".join(matched_children) 73 | reg_match.pop("Children", None) 74 | 75 | matches.append( 76 | { 77 | "Matched": matched, 78 | "Regex Pattern": reg_match, 79 | } 80 | ) 81 | 82 | return matches 83 | 84 | def clean_text(self, text): 85 | return re.sub(r"[\x00-\x1f\x7f-\x9f]", "", text) 86 | -------------------------------------------------------------------------------- /pywhat/what.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import click 4 | from rich.console import Console 5 | 6 | from pywhat import __version__, identifier, printer 7 | from pywhat.filter import Distribution, Filter 8 | from pywhat.helper import AvailableTags, InvalidTag, Keys, str_to_key 9 | 10 | 11 | def print_tags(ctx, opts, value): 12 | if value: 13 | tags = sorted(AvailableTags().get_tags()) 14 | console = Console() 15 | console.print("[bold #D7AFFF]" + "\n".join(tags) + "[/bold #D7AFFF]") 16 | sys.exit() 17 | 18 | 19 | def print_version(ctx, opts, value): 20 | if value: 21 | console = Console() 22 | console.print(f"PyWhat version [bold #49C3CE]{__version__}[/bold #49C3CE]") 23 | sys.exit() 24 | 25 | 26 | def create_filter(rarity, include, exclude): 27 | filters_dict = {} 28 | if rarity is not None: 29 | rarities = rarity.split(":") 30 | if len(rarities) != 2: 31 | print("Invalid rarity range format ('min:max' expected)") 32 | sys.exit(1) 33 | try: 34 | if not rarities[0].isspace() and rarities[0]: 35 | filters_dict["MinRarity"] = float(rarities[0]) 36 | if not rarities[1].isspace() and rarities[1]: 37 | filters_dict["MaxRarity"] = float(rarities[1]) 38 | except ValueError: 39 | print("Invalid rarity argument (float expected)") 40 | sys.exit(1) 41 | if include is not None: 42 | filters_dict["Tags"] = list(map(str.strip, include.split(","))) 43 | if exclude is not None: 44 | filters_dict["ExcludeTags"] = list(map(str.strip, exclude.split(","))) 45 | 46 | try: 47 | filter = Filter(filters_dict) 48 | except InvalidTag: 49 | print( 50 | "Passed tags are not valid.\n" 51 | "You can check available tags by using: 'pywhat --tags'" 52 | ) 53 | sys.exit(1) 54 | 55 | return filter 56 | 57 | 58 | def get_text(ctx, opts, value): 59 | if not value and not click.get_text_stream("stdin").isatty(): 60 | return click.get_text_stream("stdin").read().strip() 61 | return value 62 | 63 | 64 | @click.command( 65 | context_settings=dict( 66 | ignore_unknown_options=True, 67 | ) 68 | ) 69 | @click.argument("text_input", callback=get_text, required=False) 70 | @click.option( 71 | "-t", 72 | "--tags", 73 | is_flag=True, 74 | expose_value=False, 75 | callback=print_tags, 76 | help="Show available tags and exit.", 77 | ) 78 | @click.option( 79 | "-r", 80 | "--rarity", 81 | help="Filter by rarity. Rarity is how unlikely something is to be a false-positive. The higher the number, the more unlikely. This is in the range of 0:1. To filter only items past 0.5, use 0.5: with the colon on the end. Default 0.1:1", 82 | default="0.1:1", 83 | ) 84 | @click.option("-i", "--include", help="Only show matches with these tags.") 85 | @click.option("-e", "--exclude", help="Exclude matches with these tags.") 86 | @click.option("-o", "--only-text", is_flag=True, help="Do not scan files or folders.") 87 | @click.option("-k", "--key", help="Sort by the specified key.") 88 | @click.option("--reverse", is_flag=True, help="Sort in reverse order.") 89 | @click.option( 90 | "-br", 91 | "--boundaryless-rarity", 92 | help="Same as --rarity but for boundaryless mode (toggles what regexes will not have boundaries).", 93 | default="0.1:1", 94 | ) 95 | @click.option( 96 | "-bi", "--boundaryless-include", help="Same as --include but for boundaryless mode." 97 | ) 98 | @click.option( 99 | "-be", "--boundaryless-exclude", help="Same as --exclude but for boundaryless mode." 100 | ) 101 | @click.option( 102 | "-db", "--disable-boundaryless", is_flag=True, help="Disable boundaryless mode." 103 | ) 104 | @click.option("--json", is_flag=True, help="Return results in json format.") 105 | @click.option( 106 | "-v", 107 | "--version", 108 | is_flag=True, 109 | callback=print_version, 110 | help="Display the version of pywhat.", 111 | ) 112 | @click.option( 113 | "-if", 114 | "--include-filenames", 115 | is_flag=True, 116 | help="Search filenames for possible matches.", 117 | ) 118 | @click.option( 119 | "--format", 120 | required=False, 121 | help="Format output according to specified rules.", 122 | ) 123 | @click.option("-pt", "--print-tags", is_flag=True, help="Add flags to output") 124 | def main(**kwargs): 125 | """ 126 | pyWhat - Identify what something is. 127 | 128 | Made by Bee https://twitter.com/bee_sec_san 129 | 130 | https://github.com/bee-san 131 | 132 | Filtration: 133 | 134 | --rarity min:max 135 | 136 | Rarity is how unlikely something is to be a false-positive. The higher the number, the more unlikely. 137 | 138 | Only print entries with rarity in range [min,max]. min and max can be omitted. 139 | 140 | Note: PyWhat by default has a rarity of 0.1. To see all matches, with many potential false positives use `0:`. 141 | 142 | --include list 143 | 144 | Only include entries containing at least one tag in a list. List is a comma separated list. 145 | 146 | --exclude list 147 | 148 | Exclude specified tags. List is a comma separated list. 149 | 150 | Sorting: 151 | 152 | --key key_name 153 | 154 | Sort by the given key. 155 | 156 | --reverse 157 | 158 | Sort in reverse order. 159 | 160 | Available keys: 161 | 162 | name - Sort by the name of regex pattern 163 | 164 | rarity - Sort by rarity 165 | 166 | matched - Sort by a matched string 167 | 168 | none - No sorting is done (the default) 169 | 170 | Exporting: 171 | 172 | --json 173 | 174 | Return results in json format. 175 | 176 | Boundaryless mode: 177 | 178 | CLI tool matches strings like 'abcdTHM{hello}plze' by default because the boundaryless mode is enabled for regexes with a rarity of 0.1 and higher. 179 | 180 | Since boundaryless mode may produce a lot of false-positive matches, it is possible to disable it, either fully or partially. 181 | 182 | '--disable-boundaryless' flag can be used to fully disable this mode. 183 | 184 | In addition, '-br', '-bi', and '-be' options can be used to tweak which regexes should be in boundaryless mode. 185 | 186 | Refer to the Filtration section for more information. 187 | 188 | Formatting the output: 189 | 190 | --format format_str 191 | 192 | format_str can be equal to: 193 | 194 | pretty - Output data in the table 195 | 196 | json - Output data in json format 197 | 198 | CUSTOM_STRING - Print data in the way you want. For every match CUSTOM_STRING will be printed and '%x' (See below for possible x values) will be substituted with a match value. 199 | 200 | For example: 201 | 202 | pywhat --format '%m - %n' 'google.com htb{flag}' 203 | 204 | will print: 205 | 206 | htb{flag} - HackTheBox Flag Format 207 | google.com - Uniform Resource Locator (URL) 208 | 209 | Possible '%x' values: 210 | 211 | %m - matched text 212 | 213 | %n - name of regex 214 | 215 | %d - description (will not output if absent) 216 | 217 | %e - exploit (will not output if absent) 218 | 219 | %r - rarity 220 | 221 | %l - link (will not output if absent) 222 | 223 | %t - tags (in 'tag1, tag2 ...' format) 224 | 225 | If you want to print '%' or '\\' character - escape it: '\\%', '\\\\'. 226 | 227 | Examples: 228 | 229 | * what 'HTB{this is a flag}' 230 | 231 | * what '0x52908400098527886E0F7030069857D2E4169EE7' 232 | 233 | * what -- '52.6169586, -1.9779857' 234 | 235 | * what --rarity 0.6: 'myEmail@host.org' 236 | 237 | * what --rarity 0: --include "credentials" --exclude "aws" 'James:SecretPassword' 238 | 239 | * what -br 0.6: -be URL '123myEmail@host.org456' 240 | 241 | Your text must either be in quotation marks, or use the POSIX standard of "--" to mean "anything after -- is textual input". 242 | 243 | 244 | pyWhat can also search files or even a whole directory with recursion: 245 | 246 | * what 'secret.txt' 247 | 248 | * what 'this/is/a/path' 249 | 250 | """ 251 | if kwargs["text_input"] is None: 252 | sys.exit("Text input expected. Run 'pywhat --help' for help") 253 | dist = Distribution( 254 | create_filter(kwargs["rarity"], kwargs["include"], kwargs["exclude"]) 255 | ) 256 | if kwargs["disable_boundaryless"]: 257 | boundaryless = Filter({"Tags": []}) # use empty filter 258 | else: 259 | boundaryless = create_filter( 260 | kwargs["boundaryless_rarity"], 261 | kwargs["boundaryless_include"], 262 | kwargs["boundaryless_exclude"], 263 | ) 264 | what_obj = What_Object(dist) 265 | if kwargs["key"] is None: 266 | key = Keys.NONE 267 | else: 268 | try: 269 | key = str_to_key(kwargs["key"]) 270 | except ValueError: 271 | print("Invalid key") 272 | sys.exit(1) 273 | identified_output = what_obj.what_is_this( 274 | kwargs["text_input"], 275 | kwargs["only_text"], 276 | key, 277 | kwargs["reverse"], 278 | boundaryless, 279 | kwargs["include_filenames"], 280 | ) 281 | 282 | p = printer.Printing() 283 | 284 | if kwargs["json"] or str(kwargs["format"]).strip() == "json": 285 | p.print_json(identified_output) 286 | elif str(kwargs["format"]).strip() == "pretty": 287 | p.pretty_print(identified_output, kwargs["text_input"], kwargs["print_tags"]) 288 | elif kwargs["format"] is not None: 289 | p.format_print(identified_output, kwargs["format"]) 290 | else: 291 | p.print_raw(identified_output, kwargs["text_input"], kwargs["print_tags"]) 292 | 293 | 294 | class What_Object: 295 | def __init__(self, distribution): 296 | self.id = identifier.Identifier(dist=distribution) 297 | 298 | def what_is_this( 299 | self, 300 | text: str, 301 | only_text: bool, 302 | key, 303 | reverse: bool, 304 | boundaryless: Filter, 305 | include_filenames: bool, 306 | ) -> dict: 307 | """ 308 | Returns a Python dictionary of everything that has been identified 309 | """ 310 | return self.id.identify( 311 | text, 312 | only_text=only_text, 313 | key=key, 314 | reverse=reverse, 315 | boundaryless=boundaryless, 316 | include_filenames=include_filenames, 317 | ) 318 | 319 | 320 | if __name__ == "__main__": 321 | main() 322 | -------------------------------------------------------------------------------- /scripts/format_regex.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open("pywhat/Data/regex.json") as file: 4 | database = json.load(file) 5 | 6 | 7 | with open("pywhat/Data/regex.json", "w") as file: 8 | json.dump(database, file, indent=3) 9 | -------------------------------------------------------------------------------- /scripts/get_file_sigs.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | import requests 5 | import wikitextparser as wtp 6 | 7 | 8 | def cleanhtml(raw_html): 9 | cleanr = re.compile("<.*?>") 10 | cleantext = re.sub(cleanr, "", raw_html) 11 | return ( 12 | cleantext.replace("\n", " ") 13 | .replace("\t", " ") 14 | .replace("{", "") 15 | .replace("}", "") 16 | .replace("|", "") 17 | .replace("<", "") 18 | .replace(">", "") 19 | .replace("show file signature", "") 20 | .replace("[[", "") 21 | .replace("]]", "") 22 | .replace("web url", "") 23 | .replace("cite", "") 24 | .strip() 25 | ) 26 | 27 | 28 | r = requests.get( 29 | "https://en.wikipedia.org/w/index.php?title=" + "List_of_file_signatures&action=raw" 30 | ) 31 | wt = wtp.parse(r.text) 32 | # prints first 3 items of json, delete [0:3] to print all. 33 | 34 | sig_dict = {"root": wt.tables[0].data()} 35 | to_iter = sig_dict["root"] 36 | to_dump = [] 37 | 38 | populars = {"23 21"} 39 | 40 | for i in range(1, len(to_iter)): 41 | to_insert = {} 42 | to_insert["Hexadecimal File Signature"] = cleanhtml(to_iter[i][0]).replace(" ", "") 43 | check_iso = cleanhtml(to_iter[i][1]) 44 | if len(set(check_iso)) <= 2: 45 | to_insert["ISO 8859-1"] = None 46 | else: 47 | to_insert["ISO 8859-1"] = check_iso 48 | check = to_iter[i][3] 49 | if check == "": 50 | to_insert["Filename Extension"] = None 51 | else: 52 | to_insert["Filename Extension"] = cleanhtml(check) 53 | 54 | des = to_iter[i][4] 55 | if "url" in des: 56 | splits = des.split("=") 57 | if "|" in splits[1]: 58 | # https://wiki.wireshark.org/Development/LibpcapFileFormat#Global_Header|title 59 | split_more = splits[1].split("|") 60 | print(split_more) 61 | to_insert["URL"] = split_more[0] 62 | else: 63 | to_insert["URL"] = splits[1] 64 | to_insert["Description"] = cleanhtml(splits[0]) 65 | else: 66 | to_insert["Description"] = cleanhtml(to_iter[i][4]) 67 | 68 | if to_insert["Hexadecimal File Signature"] in populars: 69 | to_insert["Popular"] = 1 70 | else: 71 | to_insert["Popular"] = 0 72 | to_dump.append(to_insert) 73 | 74 | with open("file_signatures.json", "w") as outfile: 75 | json.dump(to_dump, outfile, indent=4) 76 | 77 | # https://en.wikipedia.org/api/rest_v1/page/html/List_of_file_signatures 78 | 79 | """ 80 | { 81 | "root": [ 82 | [ 83 | "[[Hexadecimal|Hex]] signature", 84 | "ISO 8859-1", 85 | "[[Offset (computer science)|Offset]]", 86 | "[[Filename extension]]", 87 | "Description" 88 | ], 89 | [ 90 | "
23 21
", 91 | "{{show file signature|23 21}}", 92 | "0", 93 | "", 94 | "Script or data to be passed to the program following the [[Shebang (Unix)|shebang]] (#!)" 95 | ], 96 | [ 97 | "
a1 b2 c3 d4
\n
d4 c3 b2 a1
", 98 | "{{show file signature|a1 b2 c3 d4}}\n{{show file signature|d4 c3 b2 a1}}", 99 | "0", 100 | "pcap", 101 | "Libpcap File Format{{cite web |url=https://wiki.wireshark.org/Development/LibpcapFileFormat#Global_Header|title=Libpcap File Format|access-date=2018-06-19}}" 102 | ] 103 | ] 104 | } 105 | """ 106 | -------------------------------------------------------------------------------- /scripts/get_tlds.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import requests 4 | 5 | tlds = requests.get("https://data.iana.org/TLD/tlds-alpha-by-domain.txt") 6 | final_string = "|".join(sorted(tlds.text.split("\n")[1:-1], key=len, reverse=True)) 7 | 8 | 9 | with open("pywhat/Data/regex.json", "r") as file: 10 | database = file.read() 11 | 12 | database = re.sub( 13 | r"(?:[A-Z0-9-]+\|){500,}[A-Z0-9-]+", 14 | final_string, 15 | database, 16 | ) 17 | 18 | with open("pywhat/Data/regex.json", "w") as file: 19 | file.write(database) 20 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bee-san/pyWhat/75a1592cbc4151acdaaeab78b71dd65ea9a9230e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_click.py: -------------------------------------------------------------------------------- 1 | import json 2 | import re 3 | 4 | import pytest 5 | from click.testing import CliRunner 6 | 7 | from pywhat import pywhat_tags 8 | from pywhat.what import main 9 | 10 | 11 | def test_nothing_found(): 12 | runner = CliRunner() 13 | result = runner.invoke(main, ["-db", ""]) 14 | assert result.exit_code == 0 15 | assert "Nothing found!" in result.output 16 | 17 | 18 | def test_hello_world(): 19 | runner = CliRunner() 20 | result = runner.invoke(main, ["-db", "THM{this is a flag}"]) 21 | assert result.exit_code == 0 22 | assert "THM{" in result.output 23 | 24 | 25 | def test_filtration(): 26 | runner = CliRunner() 27 | result = runner.invoke( 28 | main, 29 | ["--rarity", "0.5:", "--include", "Identifiers,Media", "-db", "fixtures/file"], 30 | ) 31 | assert result.exit_code == 0 32 | assert "THM{" not in result.output 33 | assert "ETH" not in result.output 34 | assert "Email Address" in result.output 35 | assert "IP" in result.output 36 | assert "URL" in result.output 37 | 38 | 39 | def test_tag_printing(): 40 | runner = CliRunner() 41 | result = runner.invoke(main, "--tags") 42 | assert result.exit_code == 0 43 | for tag in pywhat_tags: 44 | assert tag in result.output 45 | 46 | 47 | def test_json_printing(): 48 | """Test for valid json""" 49 | runner = CliRunner() 50 | result = runner.invoke(main, ["-db", "10.0.0.1", "--json"]) 51 | assert json.loads(result.output.replace("\n", "")) 52 | 53 | 54 | def test_json_printing2(): 55 | """Test for empty json return""" 56 | runner = CliRunner() 57 | result = runner.invoke(main, ["-db", "", "--json"]) 58 | assert result.output.strip("\n") == '{"File Signatures": null, "Regexes": null}' 59 | 60 | 61 | def test_json_printing3(): 62 | runner = CliRunner() 63 | result = runner.invoke(main, ["-db", "fixtures/file", "--json"]) 64 | assert json.loads(result.output.replace("\n", "")) 65 | 66 | 67 | def test_file_fixture(): 68 | runner = CliRunner() 69 | result = runner.invoke(main, ["-db", "fixtures/file"]) 70 | assert result.exit_code == 0 71 | assert re.findall("thm", str(result.output)) 72 | assert re.findall("Ethereum", str(result.output)) 73 | assert "Dogecoin" in result.output 74 | 75 | 76 | def test_file_fixture2(): 77 | runner = CliRunner() 78 | result = runner.invoke(main, ["-db", "fixtures/file"]) 79 | assert result.exit_code == 0 80 | assert "Dogecoin" in result.output 81 | 82 | 83 | def test_file_fixture3(): 84 | runner = CliRunner() 85 | result = runner.invoke(main, ["-db", "fixtures/file"]) 86 | assert result.exit_code == 0 87 | assert re.findall("thm", str(result.output)) 88 | 89 | 90 | def test_file_fixture4(): 91 | runner = CliRunner() 92 | result = runner.invoke(main, ["-db", "fixtures/file"]) 93 | assert result.exit_code == 0 94 | assert re.findall("Ethereum", str(result.output)) 95 | 96 | 97 | def test_file_fixture5(): 98 | runner = CliRunner() 99 | result = runner.invoke(main, ["-db", "fixtures/file"]) 100 | assert result.exit_code == 0 101 | assert re.findall("thm{", str(result.output)) 102 | 103 | 104 | def test_file_fixture7(): 105 | runner = CliRunner() 106 | result = runner.invoke(main, ["-db", "fixtures/file"]) 107 | assert result.exit_code == 0 108 | assert re.findall('thm{"', str(result.output)) 109 | 110 | 111 | def test_file_fixture8(): 112 | runner = CliRunner() 113 | result = runner.invoke(main, ["-db", "fixtures/file"]) 114 | assert result.exit_code == 0 115 | assert re.findall("URL", str(result.output)) 116 | 117 | 118 | def test_file_fixture9(): 119 | runner = CliRunner() 120 | result = runner.invoke(main, ["-db", "fixtures/file"]) 121 | assert result.exit_code == 0 122 | assert re.findall("etherscan", str(result.output)) 123 | 124 | 125 | def test_file_fixture10(): 126 | runner = CliRunner() 127 | result = runner.invoke(main, ["-db", "fixtures/file"]) 128 | assert result.exit_code == 0 129 | assert re.findall("dogechain", str(result.output)) 130 | 131 | 132 | def test_file_fixture11(): 133 | runner = CliRunner() 134 | result = runner.invoke(main, ["-db", "fixtures/file"]) 135 | assert result.exit_code == 0 136 | assert re.findall("Dogecoin", str(result.output)) 137 | 138 | 139 | def test_file_fixture12(): 140 | runner = CliRunner() 141 | result = runner.invoke(main, ["-db", "fixtures/file"]) 142 | assert result.exit_code == 0 143 | assert re.findall("Ethereum", str(result.output)) 144 | 145 | 146 | def test_file_fixture13(): 147 | runner = CliRunner() 148 | result = runner.invoke(main, ["-db", "fixtures/file"]) 149 | assert result.exit_code == 0 150 | assert re.findall("Bitcoin", str(result.output)) 151 | 152 | 153 | def test_file_fixture14(): 154 | runner = CliRunner() 155 | result = runner.invoke(main, ["-db", "fixtures/file"]) 156 | assert result.exit_code == 0 157 | assert re.findall("Nano", str(result.output)) 158 | 159 | 160 | def test_arg_parsing(): 161 | runner = CliRunner() 162 | result = runner.invoke(main, ["-db", "1KFHE7w8BhaENAswwryaoccDb6qcT6DbYY"]) 163 | assert result.exit_code == 0 164 | assert re.findall("blockchain", str(result.output)) 165 | 166 | 167 | def test_arg_parsing2(): 168 | runner = CliRunner() 169 | result = runner.invoke(main, ["http://10.1.1.1"]) 170 | assert result.exit_code == 0 171 | assert re.findall("Internet Protocol", str(result.output)) 172 | 173 | 174 | def test_file_fixture_visa(): 175 | runner = CliRunner() 176 | result = runner.invoke(main, ["-db", "fixtures/file"]) 177 | assert result.exit_code == 0 178 | assert re.findall("Visa", str(result.output)) 179 | 180 | 181 | def test_file_fixture_master_card(): 182 | runner = CliRunner() 183 | result = runner.invoke(main, ["-db", "fixtures/file"]) 184 | assert result.exit_code == 0 185 | assert re.findall("MasterCard", str(result.output)) 186 | 187 | 188 | def test_file_fixture_master_amex(): 189 | runner = CliRunner() 190 | result = runner.invoke(main, ["-db", "fixtures/file"]) 191 | assert result.exit_code == 0 192 | assert re.findall("American Express", str(result.output)) 193 | 194 | 195 | def test_file_fixture_master_diners(): 196 | runner = CliRunner() 197 | result = runner.invoke(main, ["-db", "fixtures/file"]) 198 | assert result.exit_code == 0 199 | assert re.findall("Diners Club Card", str(result.output)) 200 | 201 | 202 | def test_file_fixture_discover(): 203 | runner = CliRunner() 204 | result = runner.invoke(main, ["-db", "fixtures/file"]) 205 | assert result.exit_code == 0 206 | assert re.findall("Discover", str(result.output)) 207 | 208 | 209 | @pytest.mark.skip("Key:value turned off") 210 | def test_file_fixture_usernamepassword(): 211 | runner = CliRunner() 212 | result = runner.invoke(main, ["-db", "fixtures/file"]) 213 | assert result.exit_code == 0 214 | assert re.findall("Key", str(result.output)) 215 | 216 | 217 | def test_file_fixture_email(): 218 | runner = CliRunner() 219 | result = runner.invoke(main, ["-db", "fixtures/file"]) 220 | assert result.exit_code == 0 221 | assert re.findall("Email", str(result.output)) 222 | 223 | 224 | def test_file_fixture_email2(): 225 | runner = CliRunner() 226 | result = runner.invoke(main, ["-db", "firstname+lastname@example.com"]) 227 | assert result.exit_code == 0 228 | assert re.findall("Email", str(result.output)) 229 | 230 | 231 | def test_file_fixture_phone_number(): 232 | runner = CliRunner() 233 | result = runner.invoke(main, ["fixtures/file"]) 234 | assert result.exit_code == 0 235 | assert re.findall("Phone Number", str(result.output)) 236 | 237 | 238 | def test_file_fixture_youtube(): 239 | runner = CliRunner() 240 | result = runner.invoke(main, ["-db", "fixtures/file"]) 241 | assert result.exit_code == 0 242 | assert re.findall("YouTube", str(result.output)) 243 | 244 | 245 | def test_file_fixture_youtube_id(): 246 | runner = CliRunner() 247 | result = runner.invoke(main, ["-db", "fixtures/file"]) 248 | assert result.exit_code == 0 249 | assert re.findall("YouTube", str(result.output)) 250 | 251 | 252 | def test_file_fixture_ip4(): 253 | runner = CliRunner() 254 | result = runner.invoke(main, ["-db", "118.103.238.230"]) 255 | assert result.exit_code == 0 256 | assert re.findall("Address Version 4", str(result.output)) 257 | 258 | 259 | def test_file_fixture_ip4_shodan(): 260 | runner = CliRunner() 261 | result = runner.invoke(main, ["-db", "118.103.238.230"]) 262 | assert result.exit_code == 0 263 | assert re.findall("shodan", str(result.output)) 264 | 265 | 266 | def test_file_fixture_ip6(): 267 | runner = CliRunner() 268 | result = runner.invoke(main, ["-db", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]) 269 | assert result.exit_code == 0 270 | assert re.findall("Address Version 6", str(result.output)) 271 | 272 | 273 | def test_file_fixture_ip6_shodan(): 274 | runner = CliRunner() 275 | result = runner.invoke(main, ["-db", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]) 276 | assert result.exit_code == 0 277 | assert re.findall("shodan", str(result.output)) 278 | 279 | 280 | def test_file_fixture_ssn(): 281 | runner = CliRunner() 282 | result = runner.invoke(main, ["-db", "fixtures/file"]) 283 | assert result.exit_code == 0 284 | assert re.findall("Social", str(result.output)) 285 | 286 | 287 | @pytest.mark.skip("Key:value turned off") 288 | def test_file_pcap(): 289 | runner = CliRunner() 290 | result = runner.invoke(main, ["-db", "fixtures/FollowTheLeader.pcap"]) 291 | assert result.exit_code == 0 292 | assert re.findall("Host:", str(result.output)) 293 | 294 | 295 | def test_file_coords(): 296 | runner = CliRunner() 297 | result = runner.invoke(main, ["-db", "52.6169586, -1.9779857"]) 298 | assert result.exit_code == 0 299 | assert re.findall("Latitude", str(result.output)) 300 | 301 | 302 | def test_file_fixture_ltc(): 303 | runner = CliRunner() 304 | result = runner.invoke(main, ["-db", "fixtures/file"]) 305 | assert result.exit_code == 0 306 | assert re.findall("Litecoin", str(result.output)) 307 | 308 | 309 | def test_file_fixture_ltc2(): 310 | runner = CliRunner() 311 | result = runner.invoke(main, ["-db", "fixtures/file"]) 312 | assert result.exit_code == 0 313 | assert re.findall("live.block", str(result.output)) 314 | 315 | 316 | def test_file_fixture_bch(): 317 | runner = CliRunner() 318 | result = runner.invoke(main, ["-db", "fixtures/file"]) 319 | assert result.exit_code == 0 320 | assert re.findall("Bitcoin Cash", str(result.output)) 321 | 322 | 323 | def test_file_fixture_bch2(): 324 | runner = CliRunner() 325 | result = runner.invoke( 326 | main, ["-db", "bitcoincash:qzlg6uvceehgzgtz6phmvy8gtdqyt6vf359at4n3lq"] 327 | ) 328 | assert result.exit_code == 0 329 | assert re.findall("blockchain", str(result.output)) 330 | 331 | 332 | def test_file_fixture_xrp(): 333 | runner = CliRunner() 334 | result = runner.invoke(main, ["-db", "fixtures/file"]) 335 | assert result.exit_code == 0 336 | assert re.findall("Ripple", str(result.output)) 337 | 338 | 339 | def test_file_fixture_xrp2(): 340 | runner = CliRunner() 341 | result = runner.invoke(main, ["-db", "fixtures/file"]) 342 | assert result.exit_code == 0 343 | assert re.findall("xrpscan", str(result.output)) 344 | 345 | 346 | def test_file_fixture_xmr(): 347 | runner = CliRunner() 348 | result = runner.invoke(main, ["-db", "fixtures/file"]) 349 | assert result.exit_code == 0 350 | assert re.findall("Monero", str(result.output)) 351 | 352 | 353 | def test_file_fixture_doi(): 354 | runner = CliRunner() 355 | result = runner.invoke(main, ["-db", "fixtures/file"]) 356 | assert result.exit_code == 0 357 | assert re.findall("DOI", str(result.output)) 358 | 359 | 360 | def test_file_fixture_mailchimp(): 361 | runner = CliRunner() 362 | result = runner.invoke(main, ["-db", "fixtures/file"]) 363 | assert result.exit_code == 0 364 | assert re.findall("Mailchimp", str(result.output)) 365 | 366 | 367 | def test_file_cors(): 368 | runner = CliRunner() 369 | result = runner.invoke(main, ["-db", "Access-Control-Allow: *"]) 370 | assert result.exit_code == 0 371 | assert re.findall("Access", str(result.output)) 372 | 373 | 374 | def test_file_jwt(): 375 | runner = CliRunner() 376 | result = runner.invoke( 377 | main, 378 | [ 379 | "-db", 380 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", 381 | ], 382 | ) 383 | assert result.exit_code == 0 384 | assert re.findall("JWT", str(result.output)) 385 | 386 | 387 | def test_file_s3(): 388 | runner = CliRunner() 389 | result = runner.invoke(main, ["-db", "http://s3.amazonaws.com/bucket/"]) 390 | assert result.exit_code == 0 391 | assert re.findall("S3", str(result.output)) 392 | 393 | 394 | def test_file_s3_2(): 395 | runner = CliRunner() 396 | result = runner.invoke(main, ["-db", "s3://bucket/path/key"]) 397 | assert result.exit_code == 0 398 | assert re.findall("S3", str(result.output)) 399 | 400 | 401 | def test_file_s3_3(): 402 | runner = CliRunner() 403 | result = runner.invoke(main, ["-db", "s3://bucket/path/directory/"]) 404 | assert result.exit_code == 0 405 | assert re.findall("S3", str(result.output)) 406 | 407 | 408 | def test_file_arn(): 409 | runner = CliRunner() 410 | result = runner.invoke( 411 | main, ["-db", "arn:partition:service:region:account-id:resource"] 412 | ) 413 | assert result.exit_code == 0 414 | assert re.findall("ARN", str(result.output)) 415 | 416 | 417 | def test_file_arn2(): 418 | runner = CliRunner() 419 | result = runner.invoke( 420 | main, ["-db", "arn:partition:service:region:account-id:resourcetype/resource"] 421 | ) 422 | assert result.exit_code == 0 423 | assert re.findall("ARN", str(result.output)) 424 | 425 | 426 | def test_file_arn3(): 427 | runner = CliRunner() 428 | result = runner.invoke( 429 | main, ["-db", "arn:partition:service:region:account-id:resourcetype:resource"] 430 | ) 431 | assert result.exit_code == 0 432 | assert re.findall("ARN", str(result.output)) 433 | 434 | 435 | def test_file_arn4(): 436 | runner = CliRunner() 437 | result = runner.invoke( 438 | main, ["-db", "arn:aws:s3:::my_corporate_bucket/Development/*"] 439 | ) 440 | assert result.exit_code == 0 441 | assert re.findall("ARN", str(result.output)) 442 | 443 | 444 | def test_key_value_min_rarity_0(): 445 | runner = CliRunner() 446 | result = runner.invoke(main, ["-db", "--rarity", "0:", "key:value"]) 447 | assert result.exit_code == 0 448 | assert re.findall("Key:Value", str(result.output)) 449 | 450 | 451 | def test_key_value_min_rarity_0_1(): 452 | runner = CliRunner() 453 | result = runner.invoke(main, ["-db", "--rarity", "0:", "key : value"]) 454 | assert result.exit_code == 0 455 | assert re.findall("Key:Value", str(result.output)) 456 | 457 | 458 | def test_key_value_min_rarity_0_2(): 459 | runner = CliRunner() 460 | result = runner.invoke(main, ["-db", "--rarity", "0:", "key: value"]) 461 | assert result.exit_code == 0 462 | assert re.findall("Key:Value", str(result.output)) 463 | 464 | 465 | def test_key_value_min_rarity_0_3(): 466 | runner = CliRunner() 467 | result = runner.invoke(main, ["-db", "--rarity", "0:", ":a:"]) 468 | assert result.exit_code == 0 469 | assert not re.findall("Key:Value", str(result.output)) 470 | 471 | 472 | def test_key_value_min_rarity_0_4(): 473 | runner = CliRunner() 474 | result = runner.invoke(main, ["-db", "--rarity", "0:", ":::::"]) 475 | assert result.exit_code == 0 476 | assert not re.findall("Key:Value", str(result.output)) 477 | 478 | 479 | def test_key_value_min_rarity_0_5(): 480 | runner = CliRunner() 481 | result = runner.invoke(main, ["-db", "--rarity", "0:", "a:b:c"]) 482 | assert result.exit_code == 0 483 | assert not re.findall("a:b:c", str(result.output)) 484 | 485 | 486 | def test_key_value_min_rarity_0_6(): 487 | runner = CliRunner() 488 | result = runner.invoke( 489 | main, ["--rarity", "0:", "--boundaryless-rarity", "0:", "a:b:c"] 490 | ) 491 | assert result.exit_code == 0 492 | assert re.findall("a:b", str(result.output)) 493 | 494 | 495 | def test_key_value_min_rarity_0_7(): 496 | runner = CliRunner() 497 | result = runner.invoke( 498 | main, ["--rarity", "0:", "--boundaryless-rarity", "0:", "a : b:c"] 499 | ) 500 | assert result.exit_code == 0 501 | assert re.findall("a : b", str(result.output)) 502 | 503 | 504 | def test_only_text(): 505 | runner = CliRunner() 506 | result = runner.invoke(main, ["-o", "-db", "fixtures/file"]) 507 | assert result.exit_code == 0 508 | assert "Nothing found" in result.output 509 | 510 | 511 | def test_boundaryless(): 512 | runner = CliRunner() 513 | result = runner.invoke(main, ["-be", "identifiers, token", "abc118.103.238.230abc"]) 514 | assert result.exit_code == 0 515 | assert "Nothing found" in result.output 516 | 517 | 518 | def test_boundaryless2(): 519 | runner = CliRunner() 520 | result = runner.invoke(main, ["-bi", "media", "abc118.103.238.230abc"]) 521 | assert result.exit_code == 0 522 | assert "Nothing found" in result.output 523 | 524 | 525 | def test_boundaryless3(): 526 | runner = CliRunner() 527 | result = runner.invoke(main, ["-db", "abc118.103.238.230abc"]) 528 | assert result.exit_code == 0 529 | assert "Nothing found" in result.output 530 | 531 | 532 | def test_ssh_rsa_key(): 533 | runner = CliRunner() 534 | result = runner.invoke(main, ["-db", "fixtures/file"]) 535 | assert result.exit_code == 0 536 | assert re.findall("SSH RSA", str(result.output)) 537 | 538 | 539 | def test_ssh_ecdsa_key(): 540 | runner = CliRunner() 541 | result = runner.invoke(main, ["-db", "fixtures/file"]) 542 | assert result.exit_code == 0 543 | assert re.findall("SSH ECDSA", str(result.output)) 544 | 545 | 546 | def test_ssh_ed25519_key(): 547 | runner = CliRunner() 548 | result = runner.invoke(main, ["-db", "fixtures/file"]) 549 | assert result.exit_code == 0 550 | assert re.findall("SSH ED25519", str(result.output)) 551 | 552 | 553 | def test_asin(): 554 | runner = CliRunner() 555 | result = runner.invoke(main, ["-db", "fixtures/file"]) 556 | assert result.exit_code == 0 557 | assert re.findall("ASIN", str(result.output)) 558 | 559 | 560 | def test_mac(): 561 | runner = CliRunner() 562 | result = runner.invoke(main, ["-db", "fixtures/file"]) 563 | assert result.exit_code == 0 564 | assert re.findall("de:ad:be:ef:ca:fe", str(result.output)) 565 | assert re.findall("DE:AD:BE:EF:CA:FE", str(result.output)) 566 | 567 | 568 | def test_mac_tags(): 569 | runner = CliRunner() 570 | result = runner.invoke( 571 | main, 572 | ["--include", "Identifiers,Networking", "-db", "fixtures/file"], 573 | ) 574 | assert result.exit_code == 0 575 | assert "Ethernet" in result.output 576 | assert "IP" in result.output 577 | 578 | 579 | def test_pgp_public_key(): 580 | runner = CliRunner() 581 | result = runner.invoke(main, ["-db", "fixtures/file"]) 582 | assert result.exit_code == 0 583 | assert re.findall("PGP Public Key", str(result.output)) 584 | 585 | 586 | def test_pgp_private_key(): 587 | runner = CliRunner() 588 | result = runner.invoke(main, ["-db", "fixtures/file"]) 589 | assert result.exit_code == 0 590 | assert re.findall("PGP Private Key", str(result.output)) 591 | 592 | 593 | def test_file_fixture_turkish_car_plate(): 594 | runner = CliRunner() 595 | result = runner.invoke(main, ["--rarity", "0:", "fixtures/file"]) 596 | assert result.exit_code == 0 597 | assert re.findall("Turkish License Plate Number", str(result.output)) 598 | 599 | 600 | def test_file_fixture_date_of_birth(): 601 | runner = CliRunner() 602 | result = runner.invoke(main, ["-db", "fixtures/file"]) 603 | assert result.exit_code == 0 604 | assert re.findall("Date of Birth", str(result.output)) 605 | 606 | 607 | def test_file_fixture_turkish_id_number(): 608 | runner = CliRunner() 609 | result = runner.invoke(main, ["-db", "fixtures/file"]) 610 | assert result.exit_code == 0 611 | assert re.findall("Turkish Identification Number", str(result.output)) 612 | 613 | 614 | def test_file_fixture_turkish_tax_number(): 615 | runner = CliRunner() 616 | result = runner.invoke(main, ["--rarity", "0:", "fixtures/file"]) 617 | assert result.exit_code == 0 618 | assert re.findall("Turkish Tax Number", str(result.output)) 619 | 620 | 621 | def test_file_fixture_uuid(): 622 | runner = CliRunner() 623 | result = runner.invoke(main, ["fixtures/file"]) 624 | assert result.exit_code == 0 625 | assert re.findall("UUID", str(result.output)) 626 | 627 | 628 | def test_file_fixture_objectid(): 629 | runner = CliRunner() 630 | result = runner.invoke(main, ["--rarity", "0:", "fixtures/file"]) 631 | assert result.exit_code == 0 632 | assert re.findall("ObjectID", str(result.output)) 633 | 634 | 635 | def test_file_fixture_ulid(): 636 | runner = CliRunner() 637 | result = runner.invoke(main, ["--rarity", "0:", "fixtures/file"]) 638 | assert result.exit_code == 0 639 | assert re.findall("ULID", str(result.output)) 640 | 641 | 642 | def test_file_fixture_totp_URI(): 643 | runner = CliRunner() 644 | result = runner.invoke(main, ["fixtures/file"]) 645 | assert result.exit_code == 0 646 | assert re.findall("Time-Based One-Time Password [(]TOTP[)] URI", str(result.output)) 647 | 648 | 649 | def test_file_fixture_sshpass(): 650 | runner = CliRunner() 651 | result = runner.invoke(main, ["fixtures/file"]) 652 | assert result.exit_code == 0 653 | assert re.findall("SSHPass Clear Password Argument", str(result.output)) 654 | 655 | 656 | def test_file_fixture_slack_webhook(): 657 | runner = CliRunner() 658 | result = runner.invoke(main, ["fixtures/file"]) 659 | assert result.exit_code == 0 660 | assert re.findall("Slack Webhook", str(result.output)) 661 | 662 | 663 | def test_file_fixture_discord_webhook(): 664 | runner = CliRunner() 665 | result = runner.invoke(main, ["fixtures/file"]) 666 | assert result.exit_code == 0 667 | assert re.findall("Discord Webhook", str(result.output)) 668 | 669 | 670 | def test_file_fixture_guilded_webhook(): 671 | runner = CliRunner() 672 | result = runner.invoke(main, ["fixtures/file"]) 673 | assert result.exit_code == 0 674 | assert re.findall("Guilded Webhook", str(result.output)) 675 | 676 | 677 | def test_format(): 678 | runner = CliRunner() 679 | result = runner.invoke( 680 | main, ["-db", "--format", " json ", "rBPAQmwMrt7FDDPNyjwFgwSqbWZPf6SLkk"] 681 | ) 682 | assert result.exit_code == 0 683 | assert '"File Signatures":' in result.output 684 | 685 | 686 | def test_format2(): 687 | runner = CliRunner() 688 | result = runner.invoke( 689 | main, ["-db", "--format", " pretty ", "rBPAQmwMrt7FDDPNyjwFgwSqbWZPf6SLkk"] 690 | ) 691 | assert result.exit_code == 0 692 | assert "Possible Identification" in result.output 693 | 694 | 695 | def test_format3(): 696 | runner = CliRunner() 697 | result = runner.invoke( 698 | main, 699 | [ 700 | "-db", 701 | "--format", 702 | r"%m 2%n %d --- -%e%r %l %t \%d", 703 | "rBPAQmwMrt7FDDPNyjwFgwSqbWZPf6SLkk", 704 | ], 705 | ) 706 | assert result.exit_code == 0 707 | assert ( 708 | "rBPAQmwMrt7FDDPNyjwFgwSqbWZPf6SLkk 2Ripple (XRP) Wallet Address --- -0.3 https://xrpscan.com/account/rBPAQmwMrt7FDDPNyjwFgwSqbWZPf6SLkk Finance, Cryptocurrency Wallet, Ripple Wallet, Ripple, XRP %d" 709 | in result.output.replace("\n", "") 710 | ) 711 | 712 | 713 | def test_format4(): 714 | runner = CliRunner() 715 | result = runner.invoke( 716 | main, 717 | [ 718 | "-db", 719 | "--include", 720 | "Bug Bounty", 721 | "--format", 722 | r"\\%e %l %z", 723 | "heroku00000000-0000-0000-0000-000000000000", 724 | ], 725 | ) 726 | assert result.exit_code == 0 727 | assert ( 728 | '\\Use the command below to verify that the API key is valid:\n $ curl -X POST https://api.heroku.com/apps -H "Accept: application/vnd.heroku+json; version=3" -H "Authorization: Bearer heroku00000000-0000-0000-0000-000000000000"\n %z'.split() 729 | == result.output.split() 730 | ) 731 | 732 | 733 | def test_format5(): 734 | runner = CliRunner() 735 | result = runner.invoke(main, ["-db", "--format", r"%e", "thm{2}"]) 736 | assert result.exit_code == 0 737 | assert len(result.output) == 0 738 | 739 | 740 | def test_print_tags(): 741 | runner = CliRunner() 742 | result = runner.invoke(main, ["-db", "-pt", "thm{2}"]) 743 | assert result.exit_code == 0 744 | assert "Tags: CTF Flag" in result.output 745 | 746 | 747 | def test_print_tags2(): 748 | runner = CliRunner() 749 | result = runner.invoke( 750 | main, ["-db", "--print-tags", "--format", "pretty", "thm{2}"] 751 | ) 752 | assert result.exit_code == 0 753 | assert "Tags: CTF Flag" in result.output 754 | -------------------------------------------------------------------------------- /tests/test_filtration.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from pywhat import Distribution, Filter, pywhat_tags 4 | from pywhat.helper import CaseInsensitiveSet, InvalidTag, load_regexes 5 | 6 | regexes = load_regexes() 7 | 8 | 9 | @pytest.mark.skip( 10 | "Dist.get_regexes() returns the regex list with the default filter of 0.1:1. \ 11 | load_regexes() returns all regex without that filter. \ 12 | This fails because one of them is filtered and the other is not." 13 | ) 14 | def test_distribution(): 15 | dist = Distribution() 16 | assert regexes == dist.get_regexes() 17 | 18 | 19 | def test_distribution2(): 20 | filter = { 21 | "MinRarity": 0.3, 22 | "MaxRarity": 0.8, 23 | "Tags": ["Networking"], 24 | "ExcludeTags": ["Identifiers"], 25 | } 26 | dist = Distribution(filter) 27 | for regex in regexes: 28 | if ( 29 | 0.3 <= regex["Rarity"] <= 0.8 30 | and "Networking" in regex["Tags"] 31 | and "Identifiers" not in regex["Tags"] 32 | ): 33 | assert regex in dist.get_regexes() 34 | 35 | 36 | def test_distribution3(): 37 | filter1 = {"MinRarity": 0.3, "Tags": ["Networking"], "ExcludeTags": ["Identifiers"]} 38 | filter2 = {"MinRarity": 0.4, "MaxRarity": 0.8, "ExcludeTags": ["Media"]} 39 | dist = Distribution(filter1) & Distribution(filter2) 40 | assert dist._dict["MinRarity"] == 0.4 41 | assert dist._dict["MaxRarity"] == 0.8 42 | assert dist._dict["Tags"] == CaseInsensitiveSet(["Networking"]) 43 | assert dist._dict["ExcludeTags"] == CaseInsensitiveSet() 44 | 45 | for regex in regexes: 46 | if 0.4 <= regex["Rarity"] <= 0.8 and "Networking" in regex["Tags"]: 47 | assert regex in dist.get_regexes() 48 | 49 | 50 | def test_distribution4(): 51 | filter1 = {"MinRarity": 0.3, "Tags": ["Networking"], "ExcludeTags": ["Identifiers"]} 52 | filter2 = {"MinRarity": 0.4, "MaxRarity": 0.8, "ExcludeTags": ["Media"]} 53 | dist = Distribution(filter2) 54 | dist &= Distribution(filter1) 55 | assert dist._dict["MinRarity"] == 0.4 56 | assert dist._dict["MaxRarity"] == 0.8 57 | assert dist._dict["Tags"] == CaseInsensitiveSet(["Networking"]) 58 | assert dist._dict["ExcludeTags"] == CaseInsensitiveSet() 59 | 60 | for regex in regexes: 61 | if 0.4 <= regex["Rarity"] <= 0.8 and "Networking" in regex["Tags"]: 62 | assert regex in dist.get_regexes() 63 | 64 | 65 | def test_distribution5(): 66 | filter1 = {"MinRarity": 0.3, "Tags": ["Networking"], "ExcludeTags": ["Identifiers"]} 67 | filter2 = {"MinRarity": 0.4, "MaxRarity": 0.8, "ExcludeTags": ["Media"]} 68 | dist = Distribution(filter1) | Distribution(filter2) 69 | assert dist._dict["MinRarity"] == 0.3 70 | assert dist._dict["MaxRarity"] == 1 71 | assert dist._dict["Tags"] == CaseInsensitiveSet(pywhat_tags) 72 | assert dist._dict["ExcludeTags"] == CaseInsensitiveSet(["Identifiers", "Media"]) 73 | 74 | for regex in regexes: 75 | if ( 76 | 0.3 <= regex["Rarity"] <= 1 77 | and "Identifiers" not in regex["Tags"] 78 | and "Media" not in regex["Tags"] 79 | ): 80 | assert regex in dist.get_regexes() 81 | 82 | 83 | def test_distribution6(): 84 | filter1 = {"MinRarity": 0.3, "Tags": ["Networking"], "ExcludeTags": ["Identifiers"]} 85 | filter2 = {"MinRarity": 0.4, "MaxRarity": 0.8, "ExcludeTags": ["Media"]} 86 | dist = Distribution(filter2) 87 | dist |= Distribution(filter1) 88 | assert dist._dict["MinRarity"] == 0.3 89 | assert dist._dict["MaxRarity"] == 1 90 | assert dist._dict["Tags"] == CaseInsensitiveSet(pywhat_tags) 91 | assert dist._dict["ExcludeTags"] == CaseInsensitiveSet(["Identifiers", "Media"]) 92 | 93 | for regex in regexes: 94 | if ( 95 | 0.3 <= regex["Rarity"] <= 1 96 | and "Identifiers" not in regex["Tags"] 97 | and "Media" not in regex["Tags"] 98 | ): 99 | assert regex in dist.get_regexes() 100 | 101 | 102 | def test_distribution7(): 103 | with pytest.raises(InvalidTag): 104 | Distribution({"Tags": "Media", "MinRarity": 0.7}) 105 | 106 | 107 | def test_filter(): 108 | filter = { 109 | "MinRarity": 0.3, 110 | "MaxRarity": 0.8, 111 | "Tags": ["Networking"], 112 | "ExcludeTags": ["Identifiers"], 113 | } 114 | filt = Filter(filter) 115 | assert filt["MinRarity"] == 0.3 116 | assert filt["MaxRarity"] == 0.8 117 | assert filt["Tags"] == CaseInsensitiveSet(["networking"]) 118 | assert filt["ExcludeTags"] == CaseInsensitiveSet(["identifiers"]) 119 | 120 | 121 | def test_filter2(): 122 | filter1 = { 123 | "MinRarity": 0.3, 124 | "MaxRarity": 0.8, 125 | "Tags": ["Networking"], 126 | "ExcludeTags": ["Identifiers"], 127 | } 128 | filter2 = {"MinRarity": 0.5, "Tags": ["Networking", "Identifiers"]} 129 | filt = Filter(filter1) & Filter(filter2) 130 | assert filt["MinRarity"] == 0.5 131 | assert filt["MaxRarity"] == 0.8 132 | assert filt["Tags"] == CaseInsensitiveSet(["networking"]) 133 | assert filt["ExcludeTags"] == CaseInsensitiveSet([]) 134 | 135 | 136 | def test_filter3(): 137 | filter = { 138 | "MinRarity": 0.3, 139 | "MaxRarity": 0.8, 140 | "Tags": ["Networking"], 141 | "ExcludeTags": ["Identifiers"], 142 | } 143 | filt = Filter(filter) 144 | dist = Distribution(filt) 145 | for regex in regexes: 146 | if ( 147 | 0.3 <= regex["Rarity"] <= 0.8 148 | and "Networking" in regex["Tags"] 149 | and "Identifiers" not in regex["Tags"] 150 | ): 151 | assert regex in dist.get_regexes() 152 | -------------------------------------------------------------------------------- /tests/test_identifier.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from pywhat import identifier 4 | from pywhat.filter import Distribution, Filter 5 | from pywhat.helper import Keys 6 | 7 | r = identifier.Identifier() 8 | 9 | 10 | def test_identifier_works(): 11 | out = r.identify("DANHz6EQVoWyZ9rER56DwTXHWUxfkv9k2o") 12 | assert ( 13 | "Dogecoin (DOGE) Wallet Address" 14 | in out["Regexes"]["text"][0]["Regex Pattern"]["Name"] 15 | ) 16 | 17 | 18 | def test_identifier_works2(): 19 | out = r.identify("fixtures/file", only_text=False) 20 | assert "Ethereum (ETH) Wallet Address" in str(out) 21 | 22 | 23 | def test_identifier_works3(): 24 | out = r.identify("fixtures/file", only_text=False) 25 | assert "Dogecoin (DOGE) Wallet Address" in str(out) 26 | 27 | 28 | def test_identifier_filtration(): 29 | filter = {"Tags": ["Credentials"]} 30 | r = identifier.Identifier(dist=Distribution(filter)) 31 | regexes = r.identify("fixtures/file", only_text=False)["Regexes"]["file"] 32 | for regex in regexes: 33 | assert "Credentials" in regex["Regex Pattern"]["Tags"] 34 | 35 | 36 | def test_identifier_filtration2(): 37 | filter1 = {"ExcludeTags": ["Identifiers"]} 38 | filter2 = {"Tags": ["Identifiers"], "MinRarity": 0.6} 39 | r = identifier.Identifier(dist=Distribution(filter1)) 40 | regexes = r.identify("fixtures/file", only_text=False, dist=Distribution(filter2))[ 41 | "Regexes" 42 | ]["file"] 43 | for regex in regexes: 44 | assert "Identifiers" in regex["Regex Pattern"]["Tags"] 45 | assert regex["Regex Pattern"]["Rarity"] >= 0.6 46 | 47 | 48 | def test_identifier_sorting(): 49 | r = identifier.Identifier(key=Keys.NAME, reverse=True) 50 | out = r.identify("fixtures/file", only_text=False) 51 | assert out["Regexes"]["file"] 52 | 53 | 54 | def test_identifier_sorting2(): 55 | out = r.identify("fixtures/file", only_text=False, key=Keys.RARITY, reverse=True) 56 | prev = None 57 | for match in out["Regexes"]["file"]: 58 | if prev is not None: 59 | assert prev >= match["Regex Pattern"]["Rarity"] 60 | prev = match["Regex Pattern"]["Rarity"] 61 | 62 | 63 | def test_identifier_sorting3(): 64 | out = r.identify("fixtures/file", only_text=False, key=Keys.NAME) 65 | prev = None 66 | for match in out["Regexes"]["file"]: 67 | if prev is not None: 68 | assert prev <= match["Regex Pattern"]["Name"] 69 | prev = match["Regex Pattern"]["Name"] 70 | 71 | 72 | def test_identifier_sorting4(): 73 | r = identifier.Identifier(key=Keys.NAME, reverse=True) 74 | out = r.identify("fixtures/file", only_text=False) 75 | prev = None 76 | for match in out["Regexes"]["file"]: 77 | if prev is not None: 78 | assert prev >= match["Regex Pattern"]["Name"] 79 | prev = match["Regex Pattern"]["Name"] 80 | 81 | 82 | def test_identifier_sorting5(): 83 | out = r.identify("fixtures/file", only_text=False, key=Keys.MATCHED) 84 | prev = None 85 | for match in out["Regexes"]["file"]: 86 | if prev is not None: 87 | assert prev <= match["Matched"] 88 | prev = match["Matched"] 89 | 90 | 91 | def test_identifier_sorting6(): 92 | out = r.identify("fixtures/file", only_text=False, key=Keys.MATCHED, reverse=True) 93 | prev = None 94 | for match in out["Regexes"]["file"]: 95 | if prev is not None: 96 | assert prev >= match["Matched"] 97 | prev = match["Matched"] 98 | 99 | 100 | def test_only_text(): 101 | out = r.identify("fixtures/file") 102 | assert out["Regexes"] is None 103 | 104 | out = r.identify("THM{7281j}}", only_text=True) 105 | assert "TryHackMe Flag Format" in out["Regexes"]["text"][0]["Regex Pattern"]["Name"] 106 | 107 | 108 | def test_recursion(): 109 | out = r.identify("fixtures", only_text=False) 110 | 111 | assert re.findall(r"\'(?:\/|\\\\)file\'", str(list(out["Regexes"].keys()))) 112 | assert re.findall( 113 | r"\'(?:\/|\\\\)test(?:\/|\\\\)file\'", str(list(out["Regexes"].keys())) 114 | ) 115 | 116 | 117 | def test_boundaryless(): 118 | r = identifier.Identifier(boundaryless=Filter()) 119 | out = r.identify("127.0.0.1abrakadabra") 120 | assert ( 121 | "Internet Protocol (IP) Address Version 4" 122 | in out["Regexes"]["text"][0]["Regex Pattern"]["Name"] 123 | ) 124 | out = r.identify("127.0.0.1abrakadabra", boundaryless=Filter({"Tags": ["Media"]})) 125 | assert out["Regexes"] is None 126 | 127 | 128 | def test_finditer(): 129 | r = identifier.Identifier(boundaryless=Filter()) 130 | out = r.identify("anon@random.org dad@gmail.com") 131 | assert "anon@random.org" in out["Regexes"]["text"][2]["Matched"] 132 | assert "dad@gmail.com" in out["Regexes"]["text"][3]["Matched"] 133 | -------------------------------------------------------------------------------- /tests/test_regex_database.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import pytest 4 | 5 | from pywhat.helper import load_regexes 6 | 7 | database = load_regexes() 8 | 9 | 10 | @pytest.mark.skip( 11 | reason="Not all regex have tests now, check https://github.com/bee-san/pyWhat/pull/146#issuecomment-927087231 for info." 12 | ) 13 | def test_if_all_tests_exist(): 14 | with open("tests/test_regex_identifier.py", "r", encoding="utf-8") as file: 15 | tests = file.read() 16 | 17 | for regex in database: 18 | assert ( 19 | regex["Name"] in tests 20 | ), "No test for this regex found in 'test_regex_identifier.py'. Note that a test needs to assert the whole name." 21 | 22 | 23 | def test_name_capitalization(): 24 | for entry in database: 25 | entry_name = entry["Name"] 26 | for word in entry_name.split(): 27 | upper_and_num_count = sum(1 for c in word if c.isupper() or c.isnumeric()) 28 | if upper_and_num_count > 0: 29 | continue 30 | cleaned_word = word.translate({ord(c): None for c in "(),."}) 31 | if cleaned_word in ["a", "of", "etc"]: 32 | continue 33 | 34 | assert word.title() == word, ( 35 | f'Wrong capitalization in regex name: "{entry_name}"\n' 36 | f'Expected: "{entry_name.title()}"\n' 37 | "Please capitalize the first letter of each word." 38 | ) 39 | 40 | 41 | def test_regex_format(): 42 | for regex in database: 43 | assert re.findall( 44 | r"^(?:\(\?i\))?\^\(.*\)\$$", regex["Regex"] 45 | ), r"Please use ^(regex)$ regex format. If there is '\n' character, you have to escape it. If there is '(?i)', it is allowed and should be before the '^'." 46 | 47 | assert ( 48 | re.findall(r"\^\||\|\^|\$\|\^|\$\||\|\$", regex["Regex"]) == [] 49 | ), "Remove in-between boundaries. For example, '^|$' should only be '|'." 50 | 51 | 52 | def test_check_keys(): 53 | for entry in database: 54 | for key in [ 55 | "Name", 56 | "Regex", 57 | "plural_name", 58 | "Description", 59 | "Rarity", 60 | "URL", 61 | "Tags", 62 | # "Examples", # TODO 63 | ]: 64 | assert key in entry, f"{key} is missing in {entry['Name']}" 65 | 66 | 67 | def test_sorted_by_rarity(): 68 | rarity_num = [regex["Rarity"] for regex in database] 69 | 70 | assert rarity_num == sorted( 71 | rarity_num, reverse=True 72 | ), "Regexes should be sorted by rarity in 'regex.json'. Regexes with rarity '1' are at the top of the file and '0' is at the bottom." 73 | 74 | 75 | def test_no_duplicate_regexes(): 76 | names = [regex["Name"] for regex in database] 77 | duplicate_names = {name for name in names if names.count(name) > 1} 78 | assert duplicate_names == set(), ( 79 | ", ".join(duplicate_names) + " present in 'regex.json' more than once." 80 | ) 81 | -------------------------------------------------------------------------------- /tests/test_regex_identifier.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from pywhat import regex_identifier 4 | from pywhat.filter import Distribution, Filter 5 | from pywhat.helper import load_regexes 6 | 7 | database = load_regexes() 8 | r = regex_identifier.RegexIdentifier() 9 | dist = Distribution(Filter({"MinRarity": 0.0})) 10 | 11 | 12 | def test_regex_successfully_parses(): 13 | regexes = r.distribution.get_regexes() 14 | assert type(regexes) == list 15 | assert len(regexes) != 0 16 | assert all([type(regex) == dict for regex in regexes]) 17 | 18 | 19 | def regex_valid_match(name: str, match: str) -> bool: 20 | return any( 21 | name in matched["Regex Pattern"]["Name"] 22 | for matched in r.check([match], dist=dist) 23 | ) 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "name,match", 28 | [ 29 | (regex["Name"], match) 30 | for regex in database 31 | for match in regex.get("Examples", {}).get("Valid", []) 32 | ], 33 | ) 34 | def test_regex_valid_match(name: str, match: str): 35 | assert regex_valid_match(name, match) 36 | 37 | 38 | @pytest.mark.parametrize( 39 | "name,match", 40 | [ 41 | (regex["Name"], match) 42 | for regex in database 43 | for match in regex.get("Examples", {}).get("Invalid", []) 44 | ], 45 | ) 46 | def test_regex_invalid_match(name: str, match: str): 47 | assert not regex_valid_match(name, match) 48 | 49 | 50 | @pytest.mark.skip( 51 | reason="Fails because not a valid TLD. If presented in punycode, it works." 52 | ) 53 | def test_international_url(): 54 | assert regex_valid_match( 55 | "Uniform Resource Locator (URL)", r.check(["http://папироска.рф"]) 56 | ) 57 | 58 | 59 | @pytest.mark.parametrize( 60 | "match, description", 61 | [ 62 | # EUI-48 Identifier (Ethernet, WiFi, Bluetooth, etc) 63 | ("00:00:00:00:00:00", "Xerox Corp"), 64 | ("00-00-00-00-00-00", "Xerox Corp"), 65 | ("0000.0000.0000", "Xerox Corp"), 66 | # MasterCard Number 67 | ("5409010000000004", "UNION NATIONAL BANK"), 68 | ("5409 0100 0000 0004", "UNION NATIONAL BANK"), 69 | # Phone Number 70 | ("+1-202-555-0156", "United States"), 71 | ("+662025550156", "Thailand"), 72 | ("+356 202 555 0156", "Malta"), 73 | ], 74 | ) 75 | def test_match_description(match: str, description: str): 76 | assert description in r.check([match])[0]["Regex Pattern"]["Description"] 77 | 78 | 79 | @pytest.mark.parametrize( 80 | "match, exploit", 81 | [ 82 | ( 83 | "xoxp-514654431830-843187921057-792480346180-d44d2r9b71f954o8z2k5llt41ovpip6v", 84 | "https://slack.com/api/auth.test?token=xoxp-514654431830-843187921057-792480346180-d44d2r9b71f954o8z2k5llt41ovpip6v", 85 | ), 86 | ( 87 | "xoxb-51465443183-hgvhXVd2ISC2x7gaoRWBOUdQ", 88 | "https://slack.com/api/auth.test?token=xoxb-51465443183-hgvhXVd2ISC2x7gaoRWBOUdQ", 89 | ), 90 | ], 91 | ) 92 | def test_match_exploit(match: str, exploit: str): 93 | assert exploit in r.check([match])[0]["Regex Pattern"]["Exploit"] 94 | --------------------------------------------------------------------------------