[
  {
    "path": ".github/CODEOWNERS",
    "content": "*       @ericwb @lukehinds @sigmavirus24\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "custom: [\"https://psfmember.org/civicrm/contribute/transact/?reset=1&id=42\"]\ngithub: [ericwb]\ntidelift: pypi/bandit\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.md",
    "content": "---\nname: \"\\U0001F680 Feature request\"\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n\nLove this idea? Give it a 👍. We prioritize fulfilling features with the most 👍.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.yml",
    "content": "name: 🐛 Bug report\ndescription: Create a report to help us improve\nlabels: bug\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this bug report!\n\n  - type: textarea\n    id: describe-bug\n    attributes:\n      label: Describe the bug\n      description: A clear and concise description of what the bug is.\n    validations:\n      required: true\n\n  - type: textarea\n    id: reproduction-steps\n    attributes:\n      label: Reproduction steps\n      description: Steps to reproduce the behavior\n      value: |\n        1.\n        2.\n        3.\n        ...\n      render: bash\n    validations:\n      required: true\n\n  - type: textarea\n    id: expected-behavior\n    attributes:\n      label: Expected behavior\n      description: A clear and concise description of what you expected to happen.\n    validations:\n      required: true\n\n  - type: dropdown\n    id: bandit-version\n    attributes:\n      label: Bandit version\n      description: Run \"bandit --version\" if unsure of version number\n      options:\n        - 1.9.1 (Default)\n        - 1.9.0\n        - 1.8.6\n        - 1.8.5\n        - 1.8.4\n        - 1.8.3\n        - 1.8.2\n        - 1.8.1\n        - 1.8.0\n        - 1.7.10\n        - 1.7.9\n        - 1.7.8\n        - 1.7.7\n        - 1.7.6\n    validations:\n      required: true\n\n  - type: dropdown\n    id: python-version\n    attributes:\n      label: Python version\n      description: Run \"bandit --version\" if unsure of version number\n      options:\n        - \"3.14 (Default)\"\n        - \"3.13\"\n        - \"3.12\"\n        - \"3.11\"\n        - \"3.10\"\n        - \"3.9\"\n    validations:\n      required: true\n\n  - type: textarea\n    id: additional-context\n    attributes:\n      label: Additional context\n      description: Add any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: ❓ Ask a question\n    url: https://github.com/PyCQA/bandit/discussions\n    about: Please post questions in discussions.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/build-publish-image.yml",
    "content": "name: Build and Publish Bandit Images\n\non:\n  release:\n    types: [created]\n  schedule:\n    - cron: '0 0 * * 0' # Every Sunday at midnight\n  workflow_dispatch:\n\njobs:\n  build-and-publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n      id-token: write\n\n    steps:\n\n    - name: Get latest release tag\n      if: github.event_name != 'release'\n      id: get-latest-tag\n      run: |\n        TAG=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)\n        echo \"Latest tag is $TAG\"\n        echo \"RELEASE_TAG=$TAG\" >> $GITHUB_ENV\n\n    - name: Check out the repo\n      uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6\n      with:\n        ref: ${{ github.event_name == 'release' && github.ref || env.RELEASE_TAG }}\n\n    - name: Set up Docker Buildx\n      uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0\n\n    - name: Log in to GitHub Container Registry\n      uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0\n      with:\n        registry: ghcr.io\n        username: ${{ github.actor }}\n        password: ${{ secrets.GITHUB_TOKEN }}\n\n    - name: Install Cosign\n      uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0\n      with:\n        cosign-release: 'v2.2.2'\n\n    - name: Downcase github.repository value\n      run: |\n        echo \"IMAGE_NAME=`echo ${{github.repository}} | tr '[:upper:]' '[:lower:]'`\" >>${GITHUB_ENV}\n\n    - name: Build and push Docker image\n      id: build-and-push\n      uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0\n      with:\n        context: .\n        file: ./docker/Dockerfile\n        push: true\n        tags: ghcr.io/${{ env.IMAGE_NAME }}/bandit:latest\n        platforms: linux/amd64, linux/arm64, linux/arm/v7, linux/arm/v8\n\n    - name: Sign the image\n      env:\n        TAGS: ghcr.io/${{ env.IMAGE_NAME }}/bandit:latest\n        DIGEST: ${{ steps.build-and-push.outputs.digest }}\n      run: |\n        echo \"${TAGS}\" | xargs -I {} cosign sign --yes {}@${DIGEST}\n"
  },
  {
    "path": ".github/workflows/dependency-review.yml",
    "content": "name: 'Dependency Review'\non: [pull_request]\n\npermissions:\n  contents: read\n\njobs:\n  dependency-review:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      - name: 'Dependency Review'\n        uses: actions/dependency-review-action@v4\n"
  },
  {
    "path": ".github/workflows/publish-to-pypi.yml",
    "content": "name: Publish to PyPI\n\non: workflow_dispatch\n\njobs:\n  build-n-publish:\n    name: Build and publish to PyPI\n    runs-on: ubuntu-latest\n    permissions:\n      # IMPORTANT: this permission is mandatory for trusted publishing\n      id-token: write\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n    - name: Set up Python 3.10\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n\n    - name: Install dependencies\n      run: pip install tox wheel\n\n    - name: Build man page if not present\n      run: |\n        if [ ! -f doc/build/man/bandit.1 ]; then\n          tox run -e manpage\n        fi\n\n    - name: Build a binary wheel and a source tarball\n      run: |\n        python setup.py sdist bdist_wheel\n\n    - name: Publish distribution to PyPI\n      if: startsWith(github.ref, 'refs/tags')\n      uses: pypa/gh-action-pypi-publish@release/v1\n"
  },
  {
    "path": ".github/workflows/publish-to-test-pypi.yml",
    "content": "name: Publish to Test PyPI\n\non: workflow_dispatch\n\njobs:\n  build-n-publish:\n    name: Build and publish to Test PyPI\n    runs-on: ubuntu-latest\n    permissions:\n      # IMPORTANT: this permission is mandatory for trusted publishing\n      id-token: write\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        fetch-depth: 0\n    - name: Set up Python 3.10\n      uses: actions/setup-python@v6\n      with:\n        python-version: \"3.10\"\n\n    - name: Install dependencies\n      run: pip install tox wheel\n\n    - name: Build man page if not present\n      run: |\n        if [ ! -f doc/build/man/bandit.1 ]; then\n          tox run -e manpage\n        fi\n\n    - name: Build a binary wheel and a source tarball\n      run: |\n        python setup.py sdist bdist_wheel\n\n    - name: Publish distribution to Test PyPI\n      uses: pypa/gh-action-pypi-publish@release/v1\n      with:\n        repository-url: https://test.pypi.org/legacy/\n"
  },
  {
    "path": ".github/workflows/pythonpackage.yml",
    "content": "name: Build and Test Bandit\n\non: [push, pull_request]\n\njobs:\n  format:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.10\"]\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v6\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install tox\n    - name: Run tox\n      run: tox run -e format\n\n  pep8:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.10\"]\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n      with:\n        fetch-depth: 2\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v6\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install tox\n    - name: Run tox\n      run: tox run -e pep8\n\n  tests:\n    strategy:\n      matrix:\n        python-version: [\n          [\"3.10\", \"310\"],\n          [\"3.11\", \"311\"],\n          [\"3.12\", \"312\"],\n          [\"3.13\", \"313\"],\n          [\"3.14\", \"314\"],\n        ]\n        os: [ubuntu-latest, macos-latest]\n    runs-on: ${{ matrix.os }}\n    name: ${{ matrix.os }} (${{ matrix.python-version[0] }})\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v6\n    - name: Set up Python ${{ matrix.python-version[0] }}\n      uses: actions/setup-python@v6\n      with:\n        python-version: ${{ matrix.python-version[0] }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install tox\n    - name: Run tox\n      run: tox run -e py${{ matrix.python-version[1] }}\n"
  },
  {
    "path": ".gitignore",
    "content": "env*\nvenv*\n.python-version\n*.pyc\n.DS_Store\n*.egg\n*.egg-info\n.eggs/\n.idea/\n.vscode/\n.tox\n.stestr\nbuild/*\ncover/*\n.coverage*\ndoc/build/*\nChangeLog\ndoc/source/api\n.*.sw?\nAUTHORS\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "exclude: ^(examples|tools|doc)\nrepos:\n- repo: https://github.com/pre-commit/pre-commit-hooks\n  rev: v6.0.0\n  hooks:\n  - id: check-yaml\n  - id: debug-statements\n  - id: end-of-file-fixer\n  - id: trailing-whitespace\n- repo: https://github.com/asottile/reorder-python-imports\n  rev: v3.16.0\n  hooks:\n  - id: reorder-python-imports\n    args: [--application-directories, '.:src', --py38-plus]\n- repo: https://github.com/psf/black-pre-commit-mirror\n  rev: 25.12.0\n  hooks:\n  - id: black\n    args: [--line-length=79, --target-version=py38]\n- repo: https://github.com/asottile/pyupgrade\n  rev: v3.21.2\n  hooks:\n  - id: pyupgrade\n    args: [--py38-plus]\n- repo: https://github.com/jorisroovers/gitlint\n  rev: v0.19.1\n  hooks:\n  - id: gitlint\n#-   repo: https://github.com/pre-commit/mirrors-mypy\n#    rev: v0.910-1\n#    hooks:\n#    -   id: mypy\n#        exclude: ^(docs/|example-plugin/)\n"
  },
  {
    "path": ".pre-commit-hooks.yaml",
    "content": "-   id: bandit\n    name: bandit\n    description: 'Bandit is a tool for finding common security issues in Python code'\n    entry: bandit\n    language: python\n    language_version: python3\n    types: [python]\n    require_serial: true\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "version: 2\n\nbuild:\n  os: ubuntu-lts-latest\n  tools:\n    python: \"3.10\"\n\nsphinx:\n  configuration: doc/source/conf.py\n\npython:\n  install:\n    - requirements: requirements.txt\n    - requirements: doc/requirements.txt\n    - method: pip\n      path: .\n      extra_requirements:\n        - sarif\n"
  },
  {
    "path": ".stestr.conf",
    "content": "[DEFAULT]\ntest_path=./tests\ntop_dir=./\nparallel_class=True\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at Ian\nStapleton Cordasco <graffatcolmingov@gmail.com>, Ian Lee <IanLee1521@gmail.com>\nor Florian Bruhin <me@the-compiler.org>. All complaints will be reviewed and\ninvestigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Bandit\nThanks for considering to take part in the improvement of the Bandit project. Contributions are always welcome!\nHere are guidelines and rules that can be helpful if you plan to want to get involved in the project.\n\n#### Table Of Contents\n[Code of Conduct](#code-of-conduct)\n\n[How Can I Contribute?](#how-can-i-contribute)\n  * [Reporting Bugs](#reporting-bugs)\n  * [Suggesting Enhancements](#suggesting-enhancements)\n  * [Your First Code Contribution](#your-first-code-contribution)\n  * [Pull Requests](#pull-requests)\n    * [Commit Message Guidelines](#commit-message-guidelines)\n    * [Squash Commits](#squash-commits)\n  * [Things You Should Know Before Getting Started](#things-you-should-know-before-getting-started)\n    * [Vulnerability Tests](#vulnerability-tests)\n    * [Writing Tests](#writing-tests)\n    * [Extending Bandit](#extending-bandit)\n\n## Code of Conduct\nEveryone who participates in this project is governed by the PyCQA [Code of Conduct](https://github.com/PyCQA/bandit/blob/main/CODE_OF_CONDUCT.md#contributor-covenant-code-of-conduct).\n\n## Reporting Bugs\nIf you encounter a bug, please let us know about it. See the guide here [GitHub issues](https://guides.github.com/features/issues/).\n\n**Before submitting a new issue** you might want to check for an [existing issue](https://github.com/PyCQA/bandit/issues) to know if there is already a reported issue. If an issue is already open please feel free\nto add a comment to the existing issue instead of creating a new one.\n\n### Submitting your first issue\nWe encourage using the issue template to improve quality of reported issues.\nNavigate to the issues tab and select `New issue`, then select the **Bug report** template and fill out the form.\nTo submit a good bug report keep in mind to:\n* Use a descriptive title so other people can understand what the issue is about.\n* Be specific about the details, for example, what command did you use, what version of Bandit did you use, and in what environment you observed the bug (CI or development).\n\n## Suggesting Enhancements\nIf you want to suggest an enhancement, open a new issue and use the **Feature request** template.\n\n**Before submitting an enhancement** please check for existing [feature requests](https://github.com/PyCQA/bandit/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement).\n\nUseful things to point out in your feature request:\n* Explain your feature request in a way that everyone can understand\n* Please try to explain how this feature will improve the Bandit project\n\n## Your First Code Contribution\nYou can start contributing to Bandit project by picking [bug issues](https://github.com/PyCQA/bandit/issues?q=is%3Aopen+is%3Aissue+label%3Abug)\nThese issues can be easier to resolve rather than a feature request and can get you up and running with the code base.\n\n## Pull Requests\nThe best way to get started with Bandit is to grab the source:\n\nFork the repository into one with your username\n```shell script\ngit clone https://github.com/<your username>/bandit.git\n```\n\nCreate you own branch to start writing code:\n```shell script\ngit switch -c mybranch\n<create local changes>\ngit add <changed files>\ngit commit -S\n<create a good commit message>\ngit push origin mybranch\n```\nYou can test any changes with tox:\n\n```shell script\npip install tox\ntox run -e pep8\ntox run -e format\ntox run -e py310\ntox run -e docs\ntox run -e cover\n```\nIf everything is done, proceed with [opening a new pull request](https://help.github.com/en/desktop/contributing-to-projects/creating-a-pull-request)\n\n### Commit Message Guidelines\n\nWe follow the commit formatting recommendations found on [Chris Beams' How to Write a Git Commit Message article](https://chris.beams.io/posts/git-commit/).\n\nWell formed commit messages not only help reviewers understand the nature of\nthe Pull Request, but also assists the release process where commit messages\nare used to generate release notes.\n\nA good example of a commit message would be as follows:\n\n```\nSummarize changes in around 50 characters or less\n\nMore detailed explanatory text, if necessary. Wrap it to about 72\ncharacters or so. In some contexts, the first line is treated as the\nsubject of the commit and the rest of the text as the body. The\nblank line separating the summary from the body is critical (unless\nyou omit the body entirely); various tools like `log`, `shortlog`\nand `rebase` can get confused if you run the two together.\n\nExplain the problem that this commit is solving. Focus on why you\nare making this change as opposed to how (the code explains that).\nAre there side effects or other unintuitive consequences of this\nchange? Here's the place to explain them.\n\nFurther paragraphs come after blank lines.\n\n - Bullet points are okay, too\n\n - Typically a hyphen or asterisk is used for the bullet, preceded\n   by a single space, with blank lines in between, but conventions\n   vary here\n\nIf you use an issue tracker, put references to them at the bottom,\nlike this:\n\nResolves: #123\nSee also: #456, #789\n```\n\nNote the `Resolves #123` tag, this references the issue raised and allows us to\nensure issues are associated and closed when a pull request is merged.\n\nPlease refer to [the github help page on message types](https://help.github.com/articles/closing-issues-using-keywords/)\nfor a complete list of issue references.\n\n### Squash Commits\n\nShould your pull request consist of more than one commit (perhaps due to\na change being requested during the review cycle), please perform a git squash\nonce a reviewer has approved your pull request.\n\nA squash can be performed as follows. Let's say you have the following commits:\n\n    initial commit\n    second commit\n    final commit\n\nRun the command below with the number set to the total commits you wish to\nsquash (in our case 3 commits):\n\n    git rebase -i HEAD~3\n\nYou default text editor will then open up and you will see the following::\n\n    pick eb36612 initial commit\n    pick 9ac8968 second commit\n    pick a760569 final commit\n\n    # Rebase eb1429f..a760569 onto eb1429f (3 commands)\n\nWe want to rebase on top of our first commit, so we change the other two commits\nto `squash`:\n\n    pick eb36612 initial commit\n    squash 9ac8968 second commit\n    squash a760569 final commit\n\nAfter this, should you wish to update your commit message to better summarise\nall of your pull request, run:\n\n    git commit --amend\n\nYou will then need to force push (assuming your initial commit(s) were posted\nto github):\n\n    git push origin your-branch --force\n\n## Things You Should Know Before Getting Started\n\n### Vulnerability Tests\nVulnerability tests or \"plugins\" are defined in files in the plugins directory.\n\nTests are written in Python and are autodiscovered from the plugins directory.\nEach test can examine one or more type of Python statements. Tests are marked\nwith the types of Python statements they examine (for example: function call,\nstring, import, etc).\n\nTests are executed by the ``BanditNodeVisitor`` object as it visits each node\nin the AST.\n\nTest results are managed in the ``Manager`` and aggregated for\noutput at the completion of a test run through the method `output_result` from ``Manager`` instance.\n\n### Writing Tests\nTo write a test:\n - Identify a vulnerability to build a test for, and create a new file in\n   examples/ that contains one or more cases of that vulnerability.\n - Consider the vulnerability you're testing for, mark the function with one\n   or more of the appropriate decorators:\n   - @checks('Call')\n   - @checks('Import', 'ImportFrom')\n   - @checks('Str')\n - Create a new Python source file to contain your test, you can reference\n   existing tests for examples.\n - The function that you create should take a parameter \"context\" which is\n   an instance of the context class you can query for information about the\n   current element being examined.  You can also get the raw AST node for\n   more advanced use cases.  Please see the context.py file for more.\n - Extend your Bandit configuration file as needed to support your new test.\n - Execute Bandit against the test file you defined in examples/ and ensure\n   that it detects the vulnerability.  Consider variations on how this\n   vulnerability might present itself and extend the example file and the test\n   function accordingly.\n\n\n### Extending Bandit\n\nBandit allows users to write and register extensions for checks and formatters.\nBandit will load plugins from two entry-points:\n\n- `bandit.formatters`\n- `bandit.plugins`\n\nFormatters need to accept 5 things:\n\n- `manager`: an instance of `bandit manager`\n- `fileobj`: the output file object, which may be sys.stdout\n- `sev_level` : Filtering severity level\n- `conf_level`: Filtering confidence level\n- `lines=-1`: number of lines to report\n\nPlugins tend to take advantage of the `bandit.checks` decorator which allows\nthe author to register a check for a particular type of AST node. For example\n\n::\n\n    @bandit.checks('Call')\n    def prohibit_unsafe_deserialization(context):\n        if 'unsafe_load' in context.call_function_name_qual:\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                text=\"Unsafe deserialization detected.\"\n            )\n\nTo register your plugin, you have two options:\n\n1. If you're using setuptools directly, add something like the following to\n   your ``setup`` call::\n\n        # If you have an imaginary bson formatter in the bandit_bson module\n        # and a function called `formatter`.\n        entry_points={'bandit.formatters': ['bson = bandit_bson:formatter']}\n        # Or a check for using mako templates in bandit_mako that\n        entry_points={'bandit.plugins': ['mako = bandit_mako']}\n\n2. If you're using pbr, add something like the following to your `setup.cfg`\n   file::\n\n        [entry_points]\n        bandit.formatters =\n            bson = bandit_bson:formatter\n        bandit.plugins =\n            mako = bandit_mako\n\n## Creating and Publishing a Release (Maintainers)\n\n### Create the GitHub Release\n\n1. Navigate to the [Releases](https://github.com/PyCQA/bandit/releases) page\n2. Click on `Draft a new release`\n3. Under `Choose a tag` enter a new release version (typically increment the patch number) and select `Create new tag: <version> on publish`\n4. Click on `Generate release notes`\n5. Click on `Publish release`\n\n### Publish the Release to Test PyPI\n\n1. Go to `Actions` tab\n2. Click on the `Publish to Test PyPI` action\n3. Click on `Run workflow`\n4. Select `Use workflow from`, then `Tags` tab, and select `<version>`\n5. Click on `Run workflow`\n\n### Publish the Release to PyPI\n\n1. Go to `Actions` tab\n2. Click on the `Publish to PyPI` action\n3. Click on `Run workflow`\n4. Select `Use workflow from`, then `Tags` tab, and select `<version>`\n5. Click on `Run workflow`\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n"
  },
  {
    "path": "README.rst",
    "content": ".. image:: https://raw.githubusercontent.com/pycqa/bandit/main/logo/logotype-sm.png\n    :alt: Bandit\n\n======\n\n.. image:: https://github.com/PyCQA/bandit/actions/workflows/pythonpackage.yml/badge.svg?branch=main\n    :target: https://github.com/PyCQA/bandit/actions?query=workflow%3A%22Build+and+Test+Bandit%22+branch%3Amain\n    :alt: Build Status\n\n.. image:: https://readthedocs.org/projects/bandit/badge/?version=latest\n    :target: https://readthedocs.org/projects/bandit/\n    :alt: Docs Status\n\n.. image:: https://img.shields.io/pypi/v/bandit.svg\n    :target: https://pypi.org/project/bandit/\n    :alt: Latest Version\n\n.. image:: https://img.shields.io/pypi/pyversions/bandit.svg\n    :target: https://pypi.org/project/bandit/\n    :alt: Python Versions\n\n.. image:: https://img.shields.io/pypi/format/bandit.svg\n    :target: https://pypi.org/project/bandit/\n    :alt: Format\n\n.. image:: https://img.shields.io/badge/license-Apache%202-blue.svg\n    :target: https://github.com/PyCQA/bandit/blob/main/LICENSE\n    :alt: License\n\n.. image:: https://img.shields.io/discord/825463413634891776.svg\n    :target: https://discord.gg/qYxpadCgkx\n    :alt: Discord\n\nA security linter from PyCQA\n\n* Free software: Apache license\n* Documentation: https://bandit.readthedocs.io/en/latest/\n* Source: https://github.com/PyCQA/bandit\n* Bugs: https://github.com/PyCQA/bandit/issues\n* Contributing: https://github.com/PyCQA/bandit/blob/main/CONTRIBUTING.md\n\nOverview\n--------\n\nBandit is a tool designed to find common security issues in Python code. To do\nthis Bandit processes each file, builds an AST from it, and runs appropriate\nplugins against the AST nodes. Once Bandit has finished scanning all the files\nit generates a report.\n\nBandit was originally developed within the OpenStack Security Project and\nlater rehomed to PyCQA.\n\n.. image:: https://raw.githubusercontent.com/pycqa/bandit/main/bandit-terminal.png\n    :alt: Bandit Example Screen Shot\n\nShow Your Style\n---------------\n\n.. image:: https://img.shields.io/badge/security-bandit-yellow.svg\n    :target: https://github.com/PyCQA/bandit\n    :alt: Security Status\n\nUse our badge in your project's README!\n\nusing Markdown::\n\n    [![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)\n\nusing RST::\n\n    .. image:: https://img.shields.io/badge/security-bandit-yellow.svg\n        :target: https://github.com/PyCQA/bandit\n        :alt: Security Status\n\nReferences\n----------\n\nPython AST module documentation: https://docs.python.org/3/library/ast.html\n\nGreen Tree Snakes - the missing Python AST docs:\nhttps://greentreesnakes.readthedocs.org/en/latest/\n\nDocumentation of the various types of AST nodes that Bandit currently covers\nor could be extended to cover:\nhttps://greentreesnakes.readthedocs.org/en/latest/nodes.html\n\nContainer Images\n----------------\n\nBandit is available as a container image, built within the bandit repository\nusing GitHub Actions. The image is available on ghcr.io:\n\n.. code-block:: console\n\n    docker pull ghcr.io/pycqa/bandit/bandit\n\nThe image is built for the following architectures:\n\n* amd64\n* arm64\n* armv7\n* armv8\n\nTo pull a specific architecture, use the following format:\n\n.. code-block:: console\n\n    docker pull --platform=<architecture> ghcr.io/pycqa/bandit/bandit:latest\n\nEvery image is signed with sigstore cosign and it is possible to verify the\nsource of origin using the following cosign command:\n\n.. code-block:: console\n\n    cosign verify ghcr.io/pycqa/bandit/bandit:latest \\\n      --certificate-identity https://github.com/pycqa/bandit/.github/workflows/build-publish-image.yml@refs/tags/<version> \\\n      --certificate-oidc-issuer https://token.actions.githubusercontent.com\n\nWhere `<version>` is the release version of Bandit.\n\nSponsors\n--------\n\nThe development of Bandit is made possible by the following sponsors:\n\n.. list-table::\n   :width: 100%\n   :class: borderless\n\n   * - .. image:: https://avatars.githubusercontent.com/u/34240465?s=200&v=4\n          :target: https://opensource.mercedes-benz.com/\n          :alt: Mercedes-Benz\n          :width: 88\n\n     - .. image:: https://github.githubassets.com/assets/tidelift-8cea37dea8fc.svg\n          :target: https://tidelift.com/lifter/search/pypi/bandit\n          :alt: Tidelift\n          :width: 88\n\n     - .. image:: https://avatars.githubusercontent.com/u/110237746?s=200&v=4\n          :target: https://stacklok.com/\n          :alt: Stacklok\n          :width: 88\n\nIf you also ❤️ Bandit, please consider sponsoring.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\nBandit is a tool designed to find security issues, so every effort is made that Bandit itself is also\nfree of those issues. However, if you believe you have found a security vulnerability in this repository\nplease open it privately via the [Report a security vulnerability](https://github.com/PyCQA/bandit/security/advisories/new) link in the Issues tab.\n\n**Please do not report security vulnerabilities through public issues, discussions, or pull requests.**\n\nPlease also inform the [Tidelift security](https://tidelift.com/security). Tidelift will help coordinate the fix and disclosure.\n"
  },
  {
    "path": "bandit/__init__.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nfrom importlib import metadata\n\nfrom bandit.core import config  # noqa\nfrom bandit.core import context  # noqa\nfrom bandit.core import manager  # noqa\nfrom bandit.core import meta_ast  # noqa\nfrom bandit.core import node_visitor  # noqa\nfrom bandit.core import test_set  # noqa\nfrom bandit.core import tester  # noqa\nfrom bandit.core import utils  # noqa\nfrom bandit.core.constants import *  # noqa\nfrom bandit.core.issue import *  # noqa\nfrom bandit.core.test_properties import *  # noqa\n\n__author__ = metadata.metadata(\"bandit\")[\"Author\"]\n__version__ = metadata.version(\"bandit\")\n"
  },
  {
    "path": "bandit/__main__.py",
    "content": "#!/usr/bin/env python\n# SPDX-License-Identifier: Apache-2.0\n\"\"\"Bandit is a tool designed to find common security issues in Python code.\n\nBandit is a tool designed to find common security issues in Python code.\nTo do this Bandit processes each file, builds an AST from it, and runs\nappropriate plugins against the AST nodes. Once Bandit has finished\nscanning all the files it generates a report.\n\nBandit was originally developed within the OpenStack Security Project and\nlater rehomed to PyCQA.\n\nhttps://bandit.readthedocs.io/\n\"\"\"\nfrom bandit.cli import main\n\nmain.main()\n"
  },
  {
    "path": "bandit/blacklists/__init__.py",
    "content": ""
  },
  {
    "path": "bandit/blacklists/calls.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n====================================================\nBlacklist various Python calls known to be dangerous\n====================================================\n\nThis blacklist data checks for a number of Python calls known to have possible\nsecurity implications. The following blacklist tests are run against any\nfunction calls encountered in the scanned code base, triggered by encountering\nast.Call nodes.\n\nB301: pickle\n------------\n\nPickle and modules that wrap it can be unsafe when used to\ndeserialize untrusted data, possible security issue.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B301 | pickle              | - pickle.loads                     | Medium    |\n|      |                     | - pickle.load                      |           |\n|      |                     | - pickle.Unpickler                 |           |\n|      |                     | - dill.loads                       |           |\n|      |                     | - dill.load                        |           |\n|      |                     | - dill.Unpickler                   |           |\n|      |                     | - shelve.open                      |           |\n|      |                     | - shelve.DbfilenameShelf           |           |\n|      |                     | - jsonpickle.decode                |           |\n|      |                     | - jsonpickle.unpickler.decode      |           |\n|      |                     | - jsonpickle.unpickler.Unpickler   |           |\n|      |                     | - pandas.read_pickle               |           |\n+------+---------------------+------------------------------------+-----------+\n\nB302: marshal\n-------------\n\nDeserialization with the marshal module is possibly dangerous.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B302 | marshal             | - marshal.load                     | Medium    |\n|      |                     | - marshal.loads                    |           |\n+------+---------------------+------------------------------------+-----------+\n\nB303: md5\n---------\n\nUse of insecure MD2, MD4, MD5, or SHA1 hash function.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B303 | md5                 | - hashlib.md5                      | Medium    |\n|      |                     | - hashlib.sha1                     |           |\n|      |                     | - Crypto.Hash.MD2.new              |           |\n|      |                     | - Crypto.Hash.MD4.new              |           |\n|      |                     | - Crypto.Hash.MD5.new              |           |\n|      |                     | - Crypto.Hash.SHA.new              |           |\n|      |                     | - Cryptodome.Hash.MD2.new          |           |\n|      |                     | - Cryptodome.Hash.MD4.new          |           |\n|      |                     | - Cryptodome.Hash.MD5.new          |           |\n|      |                     | - Cryptodome.Hash.SHA.new          |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .hashes.MD5                      |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .hashes.SHA1                     |           |\n+------+---------------------+------------------------------------+-----------+\n\nB304 - B305: ciphers and modes\n------------------------------\n\nUse of insecure cipher or cipher mode. Replace with a known secure cipher such\nas AES.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B304 | ciphers             | - Crypto.Cipher.ARC2.new           | High      |\n|      |                     | - Crypto.Cipher.ARC4.new           |           |\n|      |                     | - Crypto.Cipher.Blowfish.new       |           |\n|      |                     | - Crypto.Cipher.DES.new            |           |\n|      |                     | - Crypto.Cipher.XOR.new            |           |\n|      |                     | - Cryptodome.Cipher.ARC2.new       |           |\n|      |                     | - Cryptodome.Cipher.ARC4.new       |           |\n|      |                     | - Cryptodome.Cipher.Blowfish.new   |           |\n|      |                     | - Cryptodome.Cipher.DES.new        |           |\n|      |                     | - Cryptodome.Cipher.XOR.new        |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .ciphers.algorithms.ARC4         |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .ciphers.algorithms.Blowfish     |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .ciphers.algorithms.IDEA         |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .ciphers.algorithms.CAST5        |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .ciphers.algorithms.SEED         |           |\n|      |                     | - cryptography.hazmat.primitives   |           |\n|      |                     |   .ciphers.algorithms.TripleDES    |           |\n+------+---------------------+------------------------------------+-----------+\n| B305 | cipher_modes        | - cryptography.hazmat.primitives   | Medium    |\n|      |                     |   .ciphers.modes.ECB               |           |\n+------+---------------------+------------------------------------+-----------+\n\nB306: mktemp_q\n--------------\n\nUse of insecure and deprecated function (mktemp).\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B306 | mktemp_q            | - tempfile.mktemp                  | Medium    |\n+------+---------------------+------------------------------------+-----------+\n\nB307: eval\n----------\n\nUse of possibly insecure function - consider using safer ast.literal_eval.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B307 | eval                | - eval                             | Medium    |\n+------+---------------------+------------------------------------+-----------+\n\nB308: mark_safe\n---------------\n\nUse of mark_safe() may expose cross-site scripting vulnerabilities and should\nbe reviewed.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B308 | mark_safe           | - django.utils.safestring.mark_safe| Medium    |\n+------+---------------------+------------------------------------+-----------+\n\nB309: httpsconnection\n---------------------\n\nThe check for this call has been removed.\n\nUse of HTTPSConnection on older versions of Python prior to 2.7.9 and 3.4.3 do\nnot provide security, see https://wiki.openstack.org/wiki/OSSN/OSSN-0033\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B309 | httpsconnection     | - httplib.HTTPSConnection          | Medium    |\n|      |                     | - http.client.HTTPSConnection      |           |\n|      |                     | - six.moves.http_client            |           |\n|      |                     |   .HTTPSConnection                 |           |\n+------+---------------------+------------------------------------+-----------+\n\nB310: urllib_urlopen\n--------------------\n\nAudit url open for permitted schemes. Allowing use of 'file:'' or custom\nschemes is often unexpected.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B310 | urllib_urlopen      | - urllib.urlopen                   | Medium    |\n|      |                     | - urllib.request.urlopen           |           |\n|      |                     | - urllib.urlretrieve               |           |\n|      |                     | - urllib.request.urlretrieve       |           |\n|      |                     | - urllib.URLopener                 |           |\n|      |                     | - urllib.request.URLopener         |           |\n|      |                     | - urllib.FancyURLopener            |           |\n|      |                     | - urllib.request.FancyURLopener    |           |\n|      |                     | - urllib2.urlopen                  |           |\n|      |                     | - urllib2.Request                  |           |\n|      |                     | - six.moves.urllib.request.urlopen |           |\n|      |                     | - six.moves.urllib.request         |           |\n|      |                     |   .urlretrieve                     |           |\n|      |                     | - six.moves.urllib.request         |           |\n|      |                     |   .URLopener                       |           |\n|      |                     | - six.moves.urllib.request         |           |\n|      |                     |   .FancyURLopener                  |           |\n+------+---------------------+------------------------------------+-----------+\n\nB311: random\n------------\n\nStandard pseudo-random generators are not suitable for security/cryptographic\npurposes. Consider using the secrets module instead:\nhttps://docs.python.org/library/secrets.html\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B311 | random              | - random.Random                    | Low       |\n|      |                     | - random.random                    |           |\n|      |                     | - random.randrange                 |           |\n|      |                     | - random.randint                   |           |\n|      |                     | - random.choice                    |           |\n|      |                     | - random.choices                   |           |\n|      |                     | - random.uniform                   |           |\n|      |                     | - random.triangular                |           |\n|      |                     | - random.randbytes                 |           |\n|      |                     | - random.randrange                 |           |\n|      |                     | - random.sample                    |           |\n|      |                     | - random.getrandbits               |           |\n+------+---------------------+------------------------------------+-----------+\n\nB312: telnetlib\n---------------\n\nTelnet-related functions are being called. Telnet is considered insecure. Use\nSSH or some other encrypted protocol.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B312 | telnetlib           | - telnetlib.\\*                     | High      |\n+------+---------------------+------------------------------------+-----------+\n\nB313 - B319: XML\n----------------\n\nMost of this is based off of Christian Heimes' work on defusedxml:\nhttps://pypi.org/project/defusedxml/#defusedxml-sax\n\nUsing various XLM methods to parse untrusted XML data is known to be vulnerable\nto XML attacks. Methods should be replaced with their defusedxml equivalents.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B313 | xml_bad_cElementTree| - xml.etree.cElementTree.parse     | Medium    |\n|      |                     | - xml.etree.cElementTree.iterparse |           |\n|      |                     | - xml.etree.cElementTree.fromstring|           |\n|      |                     | - xml.etree.cElementTree.XMLParser |           |\n+------+---------------------+------------------------------------+-----------+\n| B314 | xml_bad_ElementTree | - xml.etree.ElementTree.parse      | Medium    |\n|      |                     | - xml.etree.ElementTree.iterparse  |           |\n|      |                     | - xml.etree.ElementTree.fromstring |           |\n|      |                     | - xml.etree.ElementTree.XMLParser  |           |\n+------+---------------------+------------------------------------+-----------+\n| B315 | xml_bad_expatreader | - xml.sax.expatreader.create_parser| Medium    |\n+------+---------------------+------------------------------------+-----------+\n| B316 | xml_bad_expatbuilder| - xml.dom.expatbuilder.parse       | Medium    |\n|      |                     | - xml.dom.expatbuilder.parseString |           |\n+------+---------------------+------------------------------------+-----------+\n| B317 | xml_bad_sax         | - xml.sax.parse                    | Medium    |\n|      |                     | - xml.sax.parseString              |           |\n|      |                     | - xml.sax.make_parser              |           |\n+------+---------------------+------------------------------------+-----------+\n| B318 | xml_bad_minidom     | - xml.dom.minidom.parse            | Medium    |\n|      |                     | - xml.dom.minidom.parseString      |           |\n+------+---------------------+------------------------------------+-----------+\n| B319 | xml_bad_pulldom     | - xml.dom.pulldom.parse            | Medium    |\n|      |                     | - xml.dom.pulldom.parseString      |           |\n+------+---------------------+------------------------------------+-----------+\n\nB320: xml_bad_etree\n-------------------\n\nThe check for this call has been removed.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B320 | xml_bad_etree       | - lxml.etree.parse                 | Medium    |\n|      |                     | - lxml.etree.fromstring            |           |\n|      |                     | - lxml.etree.RestrictedElement     |           |\n|      |                     | - lxml.etree.GlobalParserTLS       |           |\n|      |                     | - lxml.etree.getDefaultParser      |           |\n|      |                     | - lxml.etree.check_docinfo         |           |\n+------+---------------------+------------------------------------+-----------+\n\nB321: ftplib\n------------\n\nFTP-related functions are being called. FTP is considered insecure. Use\nSSH/SFTP/SCP or some other encrypted protocol.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B321 | ftplib              | - ftplib.\\*                        | High      |\n+------+---------------------+------------------------------------+-----------+\n\nB322: input\n-----------\n\nThe check for this call has been removed.\n\nThe input method in Python 2 will read from standard input, evaluate and\nrun the resulting string as python source code. This is similar, though in\nmany ways worse, than using eval. On Python 2, use raw_input instead, input\nis safe in Python 3.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B322 | input               | - input                            | High      |\n+------+---------------------+------------------------------------+-----------+\n\nB323: unverified_context\n------------------------\n\nBy default, Python will create a secure, verified ssl context for use in such\nclasses as HTTPSConnection. However, it still allows using an insecure\ncontext via the _create_unverified_context that reverts to the previous\nbehavior that does not validate certificates or perform hostname checks.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B323 | unverified_context  | - ssl._create_unverified_context   | Medium    |\n+------+---------------------+------------------------------------+-----------+\n\nB325: tempnam\n--------------\n\nThe check for this call has been removed.\n\nUse of os.tempnam() and os.tmpnam() is vulnerable to symlink attacks. Consider\nusing tmpfile() instead.\n\nFor further information:\n    https://docs.python.org/2.7/library/os.html#os.tempnam\n    https://docs.python.org/3/whatsnew/3.0.html?highlight=tempnam\n    https://bugs.python.org/issue17880\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Calls                             |  Severity |\n+======+=====================+====================================+===========+\n| B325 | tempnam             | - os.tempnam                       | Medium    |\n|      |                     | - os.tmpnam                        |           |\n+------+---------------------+------------------------------------+-----------+\n\n\"\"\"\nfrom bandit.blacklists import utils\nfrom bandit.core import issue\n\n\ndef gen_blacklist():\n    \"\"\"Generate a list of items to blacklist.\n\n    Methods of this type, \"bandit.blacklist\" plugins, are used to build a list\n    of items that bandit's built in blacklisting tests will use to trigger\n    issues. They replace the older blacklist* test plugins and allow\n    blacklisted items to have a unique bandit ID for filtering and profile\n    usage.\n\n    :return: a dictionary mapping node types to a list of blacklist data\n    \"\"\"\n    sets = []\n    sets.append(\n        utils.build_conf_dict(\n            \"pickle\",\n            \"B301\",\n            issue.Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA,\n            [\n                \"pickle.loads\",\n                \"pickle.load\",\n                \"pickle.Unpickler\",\n                \"dill.loads\",\n                \"dill.load\",\n                \"dill.Unpickler\",\n                \"shelve.open\",\n                \"shelve.DbfilenameShelf\",\n                \"jsonpickle.decode\",\n                \"jsonpickle.unpickler.decode\",\n                \"jsonpickle.unpickler.Unpickler\",\n                \"pandas.read_pickle\",\n            ],\n            \"Pickle and modules that wrap it can be unsafe when used to \"\n            \"deserialize untrusted data, possible security issue.\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"marshal\",\n            \"B302\",\n            issue.Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA,\n            [\"marshal.load\", \"marshal.loads\"],\n            \"Deserialization with the marshal module is possibly dangerous.\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"md5\",\n            \"B303\",\n            issue.Cwe.BROKEN_CRYPTO,\n            [\n                \"Crypto.Hash.MD2.new\",\n                \"Crypto.Hash.MD4.new\",\n                \"Crypto.Hash.MD5.new\",\n                \"Crypto.Hash.SHA.new\",\n                \"Cryptodome.Hash.MD2.new\",\n                \"Cryptodome.Hash.MD4.new\",\n                \"Cryptodome.Hash.MD5.new\",\n                \"Cryptodome.Hash.SHA.new\",\n                \"cryptography.hazmat.primitives.hashes.MD5\",\n                \"cryptography.hazmat.primitives.hashes.SHA1\",\n            ],\n            \"Use of insecure MD2, MD4, MD5, or SHA1 hash function.\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"ciphers\",\n            \"B304\",\n            issue.Cwe.BROKEN_CRYPTO,\n            [\n                \"Crypto.Cipher.ARC2.new\",\n                \"Crypto.Cipher.ARC4.new\",\n                \"Crypto.Cipher.Blowfish.new\",\n                \"Crypto.Cipher.DES.new\",\n                \"Crypto.Cipher.XOR.new\",\n                \"Cryptodome.Cipher.ARC2.new\",\n                \"Cryptodome.Cipher.ARC4.new\",\n                \"Cryptodome.Cipher.Blowfish.new\",\n                \"Cryptodome.Cipher.DES.new\",\n                \"Cryptodome.Cipher.XOR.new\",\n                \"cryptography.hazmat.primitives.ciphers.algorithms.ARC4\",\n                \"cryptography.hazmat.primitives.ciphers.algorithms.Blowfish\",\n                \"cryptography.hazmat.primitives.ciphers.algorithms.CAST5\",\n                \"cryptography.hazmat.primitives.ciphers.algorithms.IDEA\",\n                \"cryptography.hazmat.primitives.ciphers.algorithms.SEED\",\n                \"cryptography.hazmat.primitives.ciphers.algorithms.TripleDES\",\n            ],\n            \"Use of insecure cipher {name}. Replace with a known secure\"\n            \" cipher such as AES.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"cipher_modes\",\n            \"B305\",\n            issue.Cwe.BROKEN_CRYPTO,\n            [\"cryptography.hazmat.primitives.ciphers.modes.ECB\"],\n            \"Use of insecure cipher mode {name}.\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"mktemp_q\",\n            \"B306\",\n            issue.Cwe.INSECURE_TEMP_FILE,\n            [\"tempfile.mktemp\"],\n            \"Use of insecure and deprecated function (mktemp).\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"eval\",\n            \"B307\",\n            issue.Cwe.OS_COMMAND_INJECTION,\n            [\"eval\"],\n            \"Use of possibly insecure function - consider using safer \"\n            \"ast.literal_eval.\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"mark_safe\",\n            \"B308\",\n            issue.Cwe.XSS,\n            [\"django.utils.safestring.mark_safe\"],\n            \"Use of mark_safe() may expose cross-site scripting \"\n            \"vulnerabilities and should be reviewed.\",\n        )\n    )\n\n    # skipped B309 as the check for a call to httpsconnection has been removed\n\n    sets.append(\n        utils.build_conf_dict(\n            \"urllib_urlopen\",\n            \"B310\",\n            issue.Cwe.PATH_TRAVERSAL,\n            [\n                \"urllib.request.urlopen\",\n                \"urllib.request.urlretrieve\",\n                \"urllib.request.URLopener\",\n                \"urllib.request.FancyURLopener\",\n                \"six.moves.urllib.request.urlopen\",\n                \"six.moves.urllib.request.urlretrieve\",\n                \"six.moves.urllib.request.URLopener\",\n                \"six.moves.urllib.request.FancyURLopener\",\n            ],\n            \"Audit url open for permitted schemes. Allowing use of file:/ or \"\n            \"custom schemes is often unexpected.\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"random\",\n            \"B311\",\n            issue.Cwe.INSUFFICIENT_RANDOM_VALUES,\n            [\n                \"random.Random\",\n                \"random.random\",\n                \"random.randrange\",\n                \"random.randint\",\n                \"random.choice\",\n                \"random.choices\",\n                \"random.uniform\",\n                \"random.triangular\",\n                \"random.randbytes\",\n                \"random.sample\",\n                \"random.randrange\",\n                \"random.getrandbits\",\n            ],\n            \"Standard pseudo-random generators are not suitable for \"\n            \"security/cryptographic purposes.\",\n            \"LOW\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"telnetlib\",\n            \"B312\",\n            issue.Cwe.CLEARTEXT_TRANSMISSION,\n            [\"telnetlib.Telnet\"],\n            \"Telnet-related functions are being called. Telnet is considered \"\n            \"insecure. Use SSH or some other encrypted protocol.\",\n            \"HIGH\",\n        )\n    )\n\n    # Most of this is based off of Christian Heimes' work on defusedxml:\n    #   https://pypi.org/project/defusedxml/#defusedxml-sax\n\n    xml_msg = (\n        \"Using {name} to parse untrusted XML data is known to be \"\n        \"vulnerable to XML attacks. Replace {name} with its \"\n        \"defusedxml equivalent function or make sure \"\n        \"defusedxml.defuse_stdlib() is called\"\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_cElementTree\",\n            \"B313\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\n                \"xml.etree.cElementTree.parse\",\n                \"xml.etree.cElementTree.iterparse\",\n                \"xml.etree.cElementTree.fromstring\",\n                \"xml.etree.cElementTree.XMLParser\",\n            ],\n            xml_msg,\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_ElementTree\",\n            \"B314\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\n                \"xml.etree.ElementTree.parse\",\n                \"xml.etree.ElementTree.iterparse\",\n                \"xml.etree.ElementTree.fromstring\",\n                \"xml.etree.ElementTree.XMLParser\",\n            ],\n            xml_msg,\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_expatreader\",\n            \"B315\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.sax.expatreader.create_parser\"],\n            xml_msg,\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_expatbuilder\",\n            \"B316\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.dom.expatbuilder.parse\", \"xml.dom.expatbuilder.parseString\"],\n            xml_msg,\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_sax\",\n            \"B317\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.sax.parse\", \"xml.sax.parseString\", \"xml.sax.make_parser\"],\n            xml_msg,\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_minidom\",\n            \"B318\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.dom.minidom.parse\", \"xml.dom.minidom.parseString\"],\n            xml_msg,\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"xml_bad_pulldom\",\n            \"B319\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.dom.pulldom.parse\", \"xml.dom.pulldom.parseString\"],\n            xml_msg,\n        )\n    )\n\n    # skipped B320 as the check for a call to lxml.etree has been removed\n\n    # end of XML tests\n\n    sets.append(\n        utils.build_conf_dict(\n            \"ftplib\",\n            \"B321\",\n            issue.Cwe.CLEARTEXT_TRANSMISSION,\n            [\"ftplib.FTP\"],\n            \"FTP-related functions are being called. FTP is considered \"\n            \"insecure. Use SSH/SFTP/SCP or some other encrypted protocol.\",\n            \"HIGH\",\n        )\n    )\n\n    # skipped B322 as the check for a call to input() has been removed\n\n    sets.append(\n        utils.build_conf_dict(\n            \"unverified_context\",\n            \"B323\",\n            issue.Cwe.IMPROPER_CERT_VALIDATION,\n            [\"ssl._create_unverified_context\"],\n            \"By default, Python will create a secure, verified ssl context for\"\n            \" use in such classes as HTTPSConnection. However, it still allows\"\n            \" using an insecure context via the _create_unverified_context \"\n            \"that  reverts to the previous behavior that does not validate \"\n            \"certificates or perform hostname checks.\",\n        )\n    )\n\n    # skipped B324 (used in bandit/plugins/hashlib_new_insecure_functions.py)\n\n    # skipped B325 as the check for a call to os.tempnam and os.tmpnam have\n    # been removed\n\n    return {\"Call\": sets}\n"
  },
  {
    "path": "bandit/blacklists/imports.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n======================================================\nBlacklist various Python imports known to be dangerous\n======================================================\n\nThis blacklist data checks for a number of Python modules known to have\npossible security implications. The following blacklist tests are run against\nany import statements or calls encountered in the scanned code base.\n\nNote that the XML rules listed here are mostly based off of Christian Heimes'\nwork on defusedxml: https://pypi.org/project/defusedxml/\n\nB401: import_telnetlib\n----------------------\n\nA telnet-related module is being imported. Telnet is considered insecure. Use\nSSH or some other encrypted protocol.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B401 | import_telnetlib    | - telnetlib                        | high      |\n+------+---------------------+------------------------------------+-----------+\n\nB402: import_ftplib\n-------------------\nA FTP-related module is being imported.  FTP is considered insecure. Use\nSSH/SFTP/SCP or some other encrypted protocol.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B402 | import_ftplib       | - ftplib                           | high      |\n+------+---------------------+------------------------------------+-----------+\n\nB403: import_pickle\n-------------------\n\nConsider possible security implications associated with these modules.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B403 | import_pickle       | - pickle                           | low       |\n|      |                     | - cPickle                          |           |\n|      |                     | - dill                             |           |\n|      |                     | - shelve                           |           |\n+------+---------------------+------------------------------------+-----------+\n\nB404: import_subprocess\n-----------------------\n\nConsider possible security implications associated with these modules.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B404 | import_subprocess   | - subprocess                       | low       |\n+------+---------------------+------------------------------------+-----------+\n\n\nB405: import_xml_etree\n----------------------\n\nUsing various methods to parse untrusted XML data is known to be vulnerable to\nXML attacks. Replace vulnerable imports with the equivalent defusedxml package,\nor make sure defusedxml.defuse_stdlib() is called.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B405 | import_xml_etree    | - xml.etree.cElementTree           | low       |\n|      |                     | - xml.etree.ElementTree            |           |\n+------+---------------------+------------------------------------+-----------+\n\nB406: import_xml_sax\n--------------------\n\nUsing various methods to parse untrusted XML data is known to be vulnerable to\nXML attacks. Replace vulnerable imports with the equivalent defusedxml package,\nor make sure defusedxml.defuse_stdlib() is called.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B406 | import_xml_sax      | - xml.sax                          | low       |\n+------+---------------------+------------------------------------+-----------+\n\nB407: import_xml_expat\n----------------------\n\nUsing various methods to parse untrusted XML data is known to be vulnerable to\nXML attacks. Replace vulnerable imports with the equivalent defusedxml package,\nor make sure defusedxml.defuse_stdlib() is called.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B407 | import_xml_expat    | - xml.dom.expatbuilder             | low       |\n+------+---------------------+------------------------------------+-----------+\n\nB408: import_xml_minidom\n------------------------\n\nUsing various methods to parse untrusted XML data is known to be vulnerable to\nXML attacks. Replace vulnerable imports with the equivalent defusedxml package,\nor make sure defusedxml.defuse_stdlib() is called.\n\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B408 | import_xml_minidom  | - xml.dom.minidom                  | low       |\n+------+---------------------+------------------------------------+-----------+\n\nB409: import_xml_pulldom\n------------------------\n\nUsing various methods to parse untrusted XML data is known to be vulnerable to\nXML attacks. Replace vulnerable imports with the equivalent defusedxml package,\nor make sure defusedxml.defuse_stdlib() is called.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B409 | import_xml_pulldom  | - xml.dom.pulldom                  | low       |\n+------+---------------------+------------------------------------+-----------+\n\nB410: import_lxml\n-----------------\n\nThis import blacklist has been removed. The information here has been\nleft for historical purposes.\n\nUsing various methods to parse untrusted XML data is known to be vulnerable to\nXML attacks. Replace vulnerable imports with the equivalent defusedxml package.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B410 | import_lxml         | - lxml                             | low       |\n+------+---------------------+------------------------------------+-----------+\n\nB411: import_xmlrpclib\n----------------------\n\nXMLRPC is particularly dangerous as it is also concerned with communicating\ndata over a network. Use defusedxml.xmlrpc.monkey_patch() function to\nmonkey-patch xmlrpclib and mitigate remote XML attacks.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B411 | import_xmlrpclib    | - xmlrpc                           | high      |\n+------+---------------------+------------------------------------+-----------+\n\nB412: import_httpoxy\n--------------------\nhttpoxy is a set of vulnerabilities that affect application code running in\nCGI, or CGI-like environments. The use of CGI for web applications should be\navoided to prevent this class of attack. More details are available\nat https://httpoxy.org/.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B412 | import_httpoxy      | - wsgiref.handlers.CGIHandler      | high      |\n|      |                     | - twisted.web.twcgi.CGIScript      |           |\n+------+---------------------+------------------------------------+-----------+\n\nB413: import_pycrypto\n---------------------\npycrypto library is known to have publicly disclosed buffer overflow\nvulnerability https://github.com/dlitz/pycrypto/issues/176. It is no longer\nactively maintained and has been deprecated in favor of pyca/cryptography\nlibrary.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B413 | import_pycrypto     | - Crypto.Cipher                    | high      |\n|      |                     | - Crypto.Hash                      |           |\n|      |                     | - Crypto.IO                        |           |\n|      |                     | - Crypto.Protocol                  |           |\n|      |                     | - Crypto.PublicKey                 |           |\n|      |                     | - Crypto.Random                    |           |\n|      |                     | - Crypto.Signature                 |           |\n|      |                     | - Crypto.Util                      |           |\n+------+---------------------+------------------------------------+-----------+\n\nB414: import_pycryptodome\n-------------------------\nThis import blacklist has been removed. The information here has been\nleft for historical purposes.\n\npycryptodome is a direct fork of pycrypto that has not fully addressed\nthe issues inherent in PyCrypto.  It seems to exist, mainly, as an API\ncompatible continuation of pycrypto and should be deprecated in favor\nof pyca/cryptography which has more support among the Python community.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B414 | import_pycryptodome | - Cryptodome.Cipher                | high      |\n|      |                     | - Cryptodome.Hash                  |           |\n|      |                     | - Cryptodome.IO                    |           |\n|      |                     | - Cryptodome.Protocol              |           |\n|      |                     | - Cryptodome.PublicKey             |           |\n|      |                     | - Cryptodome.Random                |           |\n|      |                     | - Cryptodome.Signature             |           |\n|      |                     | - Cryptodome.Util                  |           |\n+------+---------------------+------------------------------------+-----------+\n\nB415: import_pyghmi\n-------------------\nAn IPMI-related module is being imported. IPMI is considered insecure. Use\nan encrypted protocol.\n\n+------+---------------------+------------------------------------+-----------+\n| ID   |  Name               |  Imports                           |  Severity |\n+======+=====================+====================================+===========+\n| B415 | import_pyghmi       | - pyghmi                           | high      |\n+------+---------------------+------------------------------------+-----------+\n\n\"\"\"\nfrom bandit.blacklists import utils\nfrom bandit.core import issue\n\n\ndef gen_blacklist():\n    \"\"\"Generate a list of items to blacklist.\n\n    Methods of this type, \"bandit.blacklist\" plugins, are used to build a list\n    of items that bandit's built in blacklisting tests will use to trigger\n    issues. They replace the older blacklist* test plugins and allow\n    blacklisted items to have a unique bandit ID for filtering and profile\n    usage.\n\n    :return: a dictionary mapping node types to a list of blacklist data\n    \"\"\"\n    sets = []\n    sets.append(\n        utils.build_conf_dict(\n            \"import_telnetlib\",\n            \"B401\",\n            issue.Cwe.CLEARTEXT_TRANSMISSION,\n            [\"telnetlib\"],\n            \"A telnet-related module is being imported.  Telnet is \"\n            \"considered insecure. Use SSH or some other encrypted protocol.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_ftplib\",\n            \"B402\",\n            issue.Cwe.CLEARTEXT_TRANSMISSION,\n            [\"ftplib\"],\n            \"A FTP-related module is being imported.  FTP is considered \"\n            \"insecure. Use SSH/SFTP/SCP or some other encrypted protocol.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_pickle\",\n            \"B403\",\n            issue.Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA,\n            [\"pickle\", \"cPickle\", \"dill\", \"shelve\"],\n            \"Consider possible security implications associated with \"\n            \"{name} module.\",\n            \"LOW\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_subprocess\",\n            \"B404\",\n            issue.Cwe.OS_COMMAND_INJECTION,\n            [\"subprocess\"],\n            \"Consider possible security implications associated with the \"\n            \"subprocess module.\",\n            \"LOW\",\n        )\n    )\n\n    # Most of this is based off of Christian Heimes' work on defusedxml:\n    #   https://pypi.org/project/defusedxml/#defusedxml-sax\n\n    xml_msg = (\n        \"Using {name} to parse untrusted XML data is known to be \"\n        \"vulnerable to XML attacks. Replace {name} with the equivalent \"\n        \"defusedxml package, or make sure defusedxml.defuse_stdlib() \"\n        \"is called.\"\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_xml_etree\",\n            \"B405\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.etree.cElementTree\", \"xml.etree.ElementTree\"],\n            xml_msg,\n            \"LOW\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_xml_sax\",\n            \"B406\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.sax\"],\n            xml_msg,\n            \"LOW\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_xml_expat\",\n            \"B407\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.dom.expatbuilder\"],\n            xml_msg,\n            \"LOW\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_xml_minidom\",\n            \"B408\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.dom.minidom\"],\n            xml_msg,\n            \"LOW\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_xml_pulldom\",\n            \"B409\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xml.dom.pulldom\"],\n            xml_msg,\n            \"LOW\",\n        )\n    )\n\n    # skipped B410 as the check for import_lxml has been removed\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_xmlrpclib\",\n            \"B411\",\n            issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            [\"xmlrpc\"],\n            \"Using {name} to parse untrusted XML data is known to be \"\n            \"vulnerable to XML attacks. Use defusedxml.xmlrpc.monkey_patch() \"\n            \"function to monkey-patch xmlrpclib and mitigate XML \"\n            \"vulnerabilities.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_httpoxy\",\n            \"B412\",\n            issue.Cwe.IMPROPER_ACCESS_CONTROL,\n            [\n                \"wsgiref.handlers.CGIHandler\",\n                \"twisted.web.twcgi.CGIScript\",\n                \"twisted.web.twcgi.CGIDirectory\",\n            ],\n            \"Consider possible security implications associated with \"\n            \"{name} module.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_pycrypto\",\n            \"B413\",\n            issue.Cwe.BROKEN_CRYPTO,\n            [\n                \"Crypto.Cipher\",\n                \"Crypto.Hash\",\n                \"Crypto.IO\",\n                \"Crypto.Protocol\",\n                \"Crypto.PublicKey\",\n                \"Crypto.Random\",\n                \"Crypto.Signature\",\n                \"Crypto.Util\",\n            ],\n            \"The pyCrypto library and its module {name} are no longer actively\"\n            \" maintained and have been deprecated. \"\n            \"Consider using pyca/cryptography library.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"import_pyghmi\",\n            \"B415\",\n            issue.Cwe.CLEARTEXT_TRANSMISSION,\n            [\"pyghmi\"],\n            \"An IPMI-related module is being imported. IPMI is considered \"\n            \"insecure. Use an encrypted protocol.\",\n            \"HIGH\",\n        )\n    )\n\n    return {\"Import\": sets, \"ImportFrom\": sets, \"Call\": sets}\n"
  },
  {
    "path": "bandit/blacklists/utils.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"Utils module.\"\"\"\n\n\ndef build_conf_dict(name, bid, cwe, qualnames, message, level=\"MEDIUM\"):\n    \"\"\"Build and return a blacklist configuration dict.\"\"\"\n    return {\n        \"name\": name,\n        \"id\": bid,\n        \"cwe\": cwe,\n        \"message\": message,\n        \"qualnames\": qualnames,\n        \"level\": level,\n    }\n"
  },
  {
    "path": "bandit/cli/__init__.py",
    "content": ""
  },
  {
    "path": "bandit/cli/baseline.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\n# #############################################################################\n# Bandit Baseline is a tool that runs Bandit against a Git commit, and compares\n# the current commit findings to the parent commit findings.\n# To do this it checks out the parent commit, runs Bandit (with any provided\n# filters or profiles), checks out the current commit, runs Bandit, and then\n# reports on any new findings.\n# #############################################################################\n\"\"\"Bandit is a tool designed to find common security issues in Python code.\"\"\"\nimport argparse\nimport contextlib\nimport logging\nimport os\nimport shutil\nimport subprocess  # nosec: B404\nimport sys\nimport tempfile\n\ntry:\n    import git\nexcept ImportError:\n    git = None\n\nbandit_args = sys.argv[1:]\nbaseline_tmp_file = \"_bandit_baseline_run.json_\"\ncurrent_commit = None\ndefault_output_format = \"terminal\"\nLOG = logging.getLogger(__name__)\nrepo = None\nreport_basename = \"bandit_baseline_result\"\nvalid_baseline_formats = [\"txt\", \"html\", \"json\"]\n\n\"\"\"baseline.py\"\"\"\n\n\ndef main():\n    \"\"\"Execute Bandit.\"\"\"\n    # our cleanup function needs this and can't be passed arguments\n    global current_commit\n    global repo\n\n    parent_commit = None\n    output_format = None\n    repo = None\n    report_fname = None\n\n    init_logger()\n\n    output_format, repo, report_fname = initialize()\n\n    if not repo:\n        sys.exit(2)\n\n    # #################### Find current and parent commits ####################\n    try:\n        commit = repo.commit()\n        current_commit = commit.hexsha\n        LOG.info(\"Got current commit: [%s]\", commit.name_rev)\n\n        commit = commit.parents[0]\n        parent_commit = commit.hexsha\n        LOG.info(\"Got parent commit: [%s]\", commit.name_rev)\n\n    except git.GitCommandError:\n        LOG.error(\"Unable to get current or parent commit\")\n        sys.exit(2)\n    except IndexError:\n        LOG.error(\"Parent commit not available\")\n        sys.exit(2)\n\n    # #################### Run Bandit against both commits ####################\n    output_type = (\n        [\"-f\", \"txt\"]\n        if output_format == default_output_format\n        else [\"-o\", report_fname]\n    )\n\n    with baseline_setup() as t:\n        bandit_tmpfile = f\"{t}/{baseline_tmp_file}\"\n\n        steps = [\n            {\n                \"message\": \"Getting Bandit baseline results\",\n                \"commit\": parent_commit,\n                \"args\": bandit_args + [\"-f\", \"json\", \"-o\", bandit_tmpfile],\n            },\n            {\n                \"message\": \"Comparing Bandit results to baseline\",\n                \"commit\": current_commit,\n                \"args\": bandit_args + [\"-b\", bandit_tmpfile] + output_type,\n            },\n        ]\n\n        return_code = None\n\n        for step in steps:\n            repo.head.reset(commit=step[\"commit\"], working_tree=True)\n\n            LOG.info(step[\"message\"])\n\n            bandit_command = [\"bandit\"] + step[\"args\"]\n\n            try:\n                output = subprocess.check_output(bandit_command)  # nosec: B603\n            except subprocess.CalledProcessError as e:\n                output = e.output\n                return_code = e.returncode\n            else:\n                return_code = 0\n                output = output.decode(\"utf-8\")  # subprocess returns bytes\n\n            if return_code not in [0, 1]:\n                LOG.error(\n                    \"Error running command: %s\\nOutput: %s\\n\",\n                    bandit_args,\n                    output,\n                )\n\n    # #################### Output and exit ####################################\n    # print output or display message about written report\n    if output_format == default_output_format:\n        print(output)\n    else:\n        LOG.info(\"Successfully wrote %s\", report_fname)\n\n    # exit with the code the last Bandit run returned\n    sys.exit(return_code)\n\n\n# #################### Clean up before exit ###################################\n@contextlib.contextmanager\ndef baseline_setup():\n    \"\"\"Baseline setup by creating temp folder and resetting repo.\"\"\"\n    d = tempfile.mkdtemp()\n    yield d\n    shutil.rmtree(d, True)\n\n    if repo:\n        repo.head.reset(commit=current_commit, working_tree=True)\n\n\n# #################### Setup logging ##########################################\ndef init_logger():\n    \"\"\"Init logger.\"\"\"\n    LOG.handlers = []\n    log_level = logging.INFO\n    log_format_string = \"[%(levelname)7s ] %(message)s\"\n    logging.captureWarnings(True)\n    LOG.setLevel(log_level)\n    handler = logging.StreamHandler(sys.stdout)\n    handler.setFormatter(logging.Formatter(log_format_string))\n    LOG.addHandler(handler)\n\n\n# #################### Perform initialization and validate assumptions ########\ndef initialize():\n    \"\"\"Initialize arguments and output formats.\"\"\"\n    valid = True\n\n    # #################### Parse Args #########################################\n    parser = argparse.ArgumentParser(\n        description=\"Bandit Baseline - Generates Bandit results compared to \"\n        \"a baseline\",\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n        epilog=\"Additional Bandit arguments such as severity filtering (-ll) \"\n        \"can be added and will be passed to Bandit.\",\n    )\n    if sys.version_info >= (3, 14):\n        parser.suggest_on_error = True\n        parser.color = False\n\n    parser.add_argument(\n        \"targets\",\n        metavar=\"targets\",\n        type=str,\n        nargs=\"+\",\n        help=\"source file(s) or directory(s) to be tested\",\n    )\n\n    parser.add_argument(\n        \"-f\",\n        dest=\"output_format\",\n        action=\"store\",\n        default=\"terminal\",\n        help=\"specify output format\",\n        choices=valid_baseline_formats,\n    )\n\n    args, _ = parser.parse_known_args()\n\n    # #################### Setup Output #######################################\n    # set the output format, or use a default if not provided\n    output_format = (\n        args.output_format if args.output_format else default_output_format\n    )\n\n    if output_format == default_output_format:\n        LOG.info(\"No output format specified, using %s\", default_output_format)\n\n    # set the report name based on the output format\n    report_fname = f\"{report_basename}.{output_format}\"\n\n    # #################### Check Requirements #################################\n    if git is None:\n        LOG.error(\"Git not available, reinstall with baseline extra\")\n        valid = False\n        return (None, None, None)\n\n    try:\n        repo = git.Repo(os.getcwd())\n\n    except git.exc.InvalidGitRepositoryError:\n        LOG.error(\"Bandit baseline must be called from a git project root\")\n        valid = False\n\n    except git.exc.GitCommandNotFound:\n        LOG.error(\"Git command not found\")\n        valid = False\n\n    else:\n        if repo.is_dirty():\n            LOG.error(\n                \"Current working directory is dirty and must be \" \"resolved\"\n            )\n            valid = False\n\n    # if output format is specified, we need to be able to write the report\n    if output_format != default_output_format and os.path.exists(report_fname):\n        LOG.error(\"File %s already exists, aborting\", report_fname)\n        valid = False\n\n    # Bandit needs to be able to create this temp file\n    if os.path.exists(baseline_tmp_file):\n        LOG.error(\n            \"Temporary file %s needs to be removed prior to running\",\n            baseline_tmp_file,\n        )\n        valid = False\n\n    # we must validate -o is not provided, as it will mess up Bandit baseline\n    if \"-o\" in bandit_args:\n        LOG.error(\"Bandit baseline must not be called with the -o option\")\n        valid = False\n\n    return (output_format, repo, report_fname) if valid else (None, None, None)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "bandit/cli/config_generator.py",
    "content": "# Copyright 2015 Red Hat Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\n\"\"\"Bandit is a tool designed to find common security issues in Python code.\"\"\"\nimport argparse\nimport importlib\nimport logging\nimport os\nimport sys\n\nimport yaml\n\nfrom bandit.core import extension_loader\n\nPROG_NAME = \"bandit_conf_generator\"\nLOG = logging.getLogger(__name__)\n\n\ntemplate = \"\"\"\n### Bandit config file generated from:\n# '{cli}'\n\n### This config may optionally select a subset of tests to run or skip by\n### filling out the 'tests' and 'skips' lists given below. If no tests are\n### specified for inclusion then it is assumed all tests are desired. The skips\n### set will remove specific tests from the include set. This can be controlled\n### using the -t/-s CLI options. Note that the same test ID should not appear\n### in both 'tests' and 'skips', this would be nonsensical and is detected by\n### Bandit at runtime.\n\n# Available tests:\n{test_list}\n\n# (optional) list included test IDs here, eg '[B101, B406]':\n{test}\n\n# (optional) list skipped test IDs here, eg '[B101, B406]':\n{skip}\n\n### (optional) plugin settings - some test plugins require configuration data\n### that may be given here, per-plugin. All bandit test plugins have a built in\n### set of sensible defaults and these will be used if no configuration is\n### provided. It is not necessary to provide settings for every (or any) plugin\n### if the defaults are acceptable.\n\n{settings}\n\"\"\"\n\n\ndef init_logger():\n    \"\"\"Init logger.\"\"\"\n    LOG.handlers = []\n    log_level = logging.INFO\n    log_format_string = \"[%(levelname)5s]: %(message)s\"\n    logging.captureWarnings(True)\n    LOG.setLevel(log_level)\n    handler = logging.StreamHandler(sys.stdout)\n    handler.setFormatter(logging.Formatter(log_format_string))\n    LOG.addHandler(handler)\n\n\ndef parse_args():\n    \"\"\"Parse arguments.\"\"\"\n    help_description = \"\"\"Bandit Config Generator\n\n    This tool is used to generate an optional profile.  The profile may be used\n    to include or skip tests and override values for plugins.\n\n    When used to store an output profile, this tool will output a template that\n    includes all plugins and their default settings.  Any settings which aren't\n    being overridden can be safely removed from the profile and default values\n    will be used.  Bandit will prefer settings from the profile over the built\n    in values.\"\"\"\n\n    parser = argparse.ArgumentParser(\n        description=help_description,\n        formatter_class=argparse.RawTextHelpFormatter,\n    )\n    if sys.version_info >= (3, 14):\n        parser.suggest_on_error = True\n        parser.color = False\n\n    parser.add_argument(\n        \"--show-defaults\",\n        dest=\"show_defaults\",\n        action=\"store_true\",\n        help=\"show the default settings values for each \"\n        \"plugin but do not output a profile\",\n    )\n    parser.add_argument(\n        \"-o\",\n        \"--out\",\n        dest=\"output_file\",\n        action=\"store\",\n        help=\"output file to save profile\",\n    )\n    parser.add_argument(\n        \"-t\",\n        \"--tests\",\n        dest=\"tests\",\n        action=\"store\",\n        default=None,\n        type=str,\n        help=\"list of test names to run\",\n    )\n    parser.add_argument(\n        \"-s\",\n        \"--skip\",\n        dest=\"skips\",\n        action=\"store\",\n        default=None,\n        type=str,\n        help=\"list of test names to skip\",\n    )\n    args = parser.parse_args()\n\n    if not args.output_file and not args.show_defaults:\n        parser.print_help()\n        parser.exit(1)\n\n    return args\n\n\ndef get_config_settings():\n    \"\"\"Get configuration settings.\"\"\"\n    config = {}\n    for plugin in extension_loader.MANAGER.plugins:\n        fn_name = plugin.name\n        function = plugin.plugin\n\n        # if a function takes config...\n        if hasattr(function, \"_takes_config\"):\n            fn_module = importlib.import_module(function.__module__)\n\n            # call the config generator if it exists\n            if hasattr(fn_module, \"gen_config\"):\n                config[fn_name] = fn_module.gen_config(function._takes_config)\n\n    return yaml.safe_dump(config, default_flow_style=False)\n\n\ndef main():\n    \"\"\"Config generator to write configuration file.\"\"\"\n    init_logger()\n    args = parse_args()\n\n    yaml_settings = get_config_settings()\n\n    if args.show_defaults:\n        print(yaml_settings)\n\n    if args.output_file:\n        if os.path.exists(os.path.abspath(args.output_file)):\n            LOG.error(\"File %s already exists, exiting\", args.output_file)\n            sys.exit(2)\n\n        try:\n            with open(args.output_file, \"w\") as f:\n                skips = args.skips.split(\",\") if args.skips else []\n                tests = args.tests.split(\",\") if args.tests else []\n\n                for skip in skips:\n                    if not extension_loader.MANAGER.check_id(skip):\n                        raise RuntimeError(f\"unknown ID in skips: {skip}\")\n\n                for test in tests:\n                    if not extension_loader.MANAGER.check_id(test):\n                        raise RuntimeError(f\"unknown ID in tests: {test}\")\n\n                tpl = \"# {0} : {1}\"\n                test_list = [\n                    tpl.format(t.plugin._test_id, t.name)\n                    for t in extension_loader.MANAGER.plugins\n                ]\n\n                others = [\n                    tpl.format(k, v[\"name\"])\n                    for k, v in (\n                        extension_loader.MANAGER.blacklist_by_id.items()\n                    )\n                ]\n                test_list.extend(others)\n                test_list.sort()\n\n                contents = template.format(\n                    cli=\" \".join(sys.argv),\n                    settings=yaml_settings,\n                    test_list=\"\\n\".join(test_list),\n                    skip=\"skips: \" + str(skips) if skips else \"skips:\",\n                    test=\"tests: \" + str(tests) if tests else \"tests:\",\n                )\n                f.write(contents)\n\n        except OSError:\n            LOG.error(\"Unable to open %s for writing\", args.output_file)\n\n        except Exception as e:\n            LOG.error(\"Error: %s\", e)\n\n        else:\n            LOG.info(\"Successfully wrote profile: %s\", args.output_file)\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "bandit/cli/main.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\n\"\"\"Bandit is a tool designed to find common security issues in Python code.\"\"\"\nimport argparse\nimport fnmatch\nimport logging\nimport os\nimport sys\nimport textwrap\n\nimport bandit\nfrom bandit.core import config as b_config\nfrom bandit.core import constants\nfrom bandit.core import manager as b_manager\nfrom bandit.core import utils\n\nBASE_CONFIG = \"bandit.yaml\"\nLOG = logging.getLogger()\n\n\ndef _init_logger(log_level=logging.INFO, log_format=None):\n    \"\"\"Initialize the logger.\n\n    :param debug: Whether to enable debug mode\n    :return: An instantiated logging instance\n    \"\"\"\n    LOG.handlers = []\n\n    if not log_format:\n        # default log format\n        log_format_string = constants.log_format_string\n    else:\n        log_format_string = log_format\n\n    logging.captureWarnings(True)\n\n    LOG.setLevel(log_level)\n    handler = logging.StreamHandler(sys.stderr)\n    handler.setFormatter(logging.Formatter(log_format_string))\n    LOG.addHandler(handler)\n    LOG.debug(\"logging initialized\")\n\n\ndef _get_options_from_ini(ini_path, target):\n    \"\"\"Return a dictionary of config options or None if we can't load any.\"\"\"\n    ini_file = None\n\n    if ini_path:\n        ini_file = ini_path\n    else:\n        bandit_files = []\n\n        for t in target:\n            for root, _, filenames in os.walk(t):\n                for filename in fnmatch.filter(filenames, \".bandit\"):\n                    bandit_files.append(os.path.join(root, filename))\n\n        if len(bandit_files) > 1:\n            LOG.error(\n                \"Multiple .bandit files found - scan separately or \"\n                \"choose one with --ini\\n\\t%s\",\n                \", \".join(bandit_files),\n            )\n            sys.exit(2)\n\n        elif len(bandit_files) == 1:\n            ini_file = bandit_files[0]\n            LOG.info(\"Found project level .bandit file: %s\", bandit_files[0])\n\n    if ini_file:\n        return utils.parse_ini_file(ini_file)\n    else:\n        return None\n\n\ndef _init_extensions():\n    from bandit.core import extension_loader as ext_loader\n\n    return ext_loader.MANAGER\n\n\ndef _log_option_source(default_val, arg_val, ini_val, option_name):\n    \"\"\"It's useful to show the source of each option.\"\"\"\n    # When default value is not defined, arg_val and ini_val is deterministic\n    if default_val is None:\n        if arg_val:\n            LOG.info(\"Using command line arg for %s\", option_name)\n            return arg_val\n        elif ini_val:\n            LOG.info(\"Using ini file for %s\", option_name)\n            return ini_val\n        else:\n            return None\n    # No value passed to command line and default value is used\n    elif default_val == arg_val:\n        return ini_val if ini_val else arg_val\n    # Certainly a value is passed to command line\n    else:\n        return arg_val\n\n\ndef _running_under_virtualenv():\n    if hasattr(sys, \"real_prefix\"):\n        return True\n    elif sys.prefix != getattr(sys, \"base_prefix\", sys.prefix):\n        return True\n\n\ndef _get_profile(config, profile_name, config_path):\n    profile = {}\n    if profile_name:\n        profiles = config.get_option(\"profiles\") or {}\n        profile = profiles.get(profile_name)\n        if profile is None:\n            raise utils.ProfileNotFound(config_path, profile_name)\n        LOG.debug(\"read in legacy profile '%s': %s\", profile_name, profile)\n    else:\n        profile[\"include\"] = set(config.get_option(\"tests\") or [])\n        profile[\"exclude\"] = set(config.get_option(\"skips\") or [])\n    return profile\n\n\ndef _log_info(args, profile):\n    inc = \",\".join([t for t in profile[\"include\"]]) or \"None\"\n    exc = \",\".join([t for t in profile[\"exclude\"]]) or \"None\"\n    LOG.info(\"profile include tests: %s\", inc)\n    LOG.info(\"profile exclude tests: %s\", exc)\n    LOG.info(\"cli include tests: %s\", args.tests)\n    LOG.info(\"cli exclude tests: %s\", args.skips)\n\n\ndef main():\n    \"\"\"Bandit CLI.\"\"\"\n    # bring our logging stuff up as early as possible\n    debug = (\n        logging.DEBUG\n        if \"-d\" in sys.argv or \"--debug\" in sys.argv\n        else logging.INFO\n    )\n    _init_logger(debug)\n    extension_mgr = _init_extensions()\n\n    baseline_formatters = [\n        f.name\n        for f in filter(\n            lambda x: hasattr(x.plugin, \"_accepts_baseline\"),\n            extension_mgr.formatters,\n        )\n    ]\n\n    # now do normal startup\n    parser = argparse.ArgumentParser(\n        description=\"Bandit - a Python source code security analyzer\",\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n    )\n    if sys.version_info >= (3, 14):\n        parser.suggest_on_error = True\n        parser.color = False\n\n    parser.add_argument(\n        \"targets\",\n        metavar=\"targets\",\n        type=str,\n        nargs=\"*\",\n        help=\"source file(s) or directory(s) to be tested\",\n    )\n    parser.add_argument(\n        \"-r\",\n        \"--recursive\",\n        dest=\"recursive\",\n        action=\"store_true\",\n        help=\"find and process files in subdirectories\",\n    )\n    parser.add_argument(\n        \"-a\",\n        \"--aggregate\",\n        dest=\"agg_type\",\n        action=\"store\",\n        default=\"file\",\n        type=str,\n        choices=[\"file\", \"vuln\"],\n        help=\"aggregate output by vulnerability (default) or by filename\",\n    )\n    parser.add_argument(\n        \"-n\",\n        \"--number\",\n        dest=\"context_lines\",\n        action=\"store\",\n        default=3,\n        type=int,\n        help=\"maximum number of code lines to output for each issue\",\n    )\n    parser.add_argument(\n        \"-c\",\n        \"--configfile\",\n        dest=\"config_file\",\n        action=\"store\",\n        default=None,\n        type=str,\n        help=\"optional config file to use for selecting plugins and \"\n        \"overriding defaults\",\n    )\n    parser.add_argument(\n        \"-p\",\n        \"--profile\",\n        dest=\"profile\",\n        action=\"store\",\n        default=None,\n        type=str,\n        help=\"profile to use (defaults to executing all tests)\",\n    )\n    parser.add_argument(\n        \"-t\",\n        \"--tests\",\n        dest=\"tests\",\n        action=\"store\",\n        default=None,\n        type=str,\n        help=\"comma-separated list of test IDs to run\",\n    )\n    parser.add_argument(\n        \"-s\",\n        \"--skip\",\n        dest=\"skips\",\n        action=\"store\",\n        default=None,\n        type=str,\n        help=\"comma-separated list of test IDs to skip\",\n    )\n    severity_group = parser.add_mutually_exclusive_group(required=False)\n    severity_group.add_argument(\n        \"-l\",\n        \"--level\",\n        dest=\"severity\",\n        action=\"count\",\n        default=1,\n        help=\"report only issues of a given severity level or \"\n        \"higher (-l for LOW, -ll for MEDIUM, -lll for HIGH)\",\n    )\n    severity_group.add_argument(\n        \"--severity-level\",\n        dest=\"severity_string\",\n        action=\"store\",\n        help=\"report only issues of a given severity level or higher.\"\n        ' \"all\" and \"low\" are likely to produce the same results, but it'\n        \" is possible for rules to be undefined which will\"\n        ' not be listed in \"low\".',\n        choices=[\"all\", \"low\", \"medium\", \"high\"],\n    )\n    confidence_group = parser.add_mutually_exclusive_group(required=False)\n    confidence_group.add_argument(\n        \"-i\",\n        \"--confidence\",\n        dest=\"confidence\",\n        action=\"count\",\n        default=1,\n        help=\"report only issues of a given confidence level or \"\n        \"higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)\",\n    )\n    confidence_group.add_argument(\n        \"--confidence-level\",\n        dest=\"confidence_string\",\n        action=\"store\",\n        help=\"report only issues of a given confidence level or higher.\"\n        ' \"all\" and \"low\" are likely to produce the same results, but it'\n        \" is possible for rules to be undefined which will\"\n        ' not be listed in \"low\".',\n        choices=[\"all\", \"low\", \"medium\", \"high\"],\n    )\n    output_format = (\n        \"screen\"\n        if (\n            sys.stdout.isatty()\n            and os.getenv(\"NO_COLOR\") is None\n            and os.getenv(\"TERM\") != \"dumb\"\n        )\n        else \"txt\"\n    )\n    parser.add_argument(\n        \"-f\",\n        \"--format\",\n        dest=\"output_format\",\n        action=\"store\",\n        default=output_format,\n        help=\"specify output format\",\n        choices=sorted(extension_mgr.formatter_names),\n    )\n    parser.add_argument(\n        \"--msg-template\",\n        action=\"store\",\n        default=None,\n        help=\"specify output message template\"\n        \" (only usable with --format custom),\"\n        \" see CUSTOM FORMAT section\"\n        \" for list of available values\",\n    )\n    parser.add_argument(\n        \"-o\",\n        \"--output\",\n        dest=\"output_file\",\n        action=\"store\",\n        nargs=\"?\",\n        type=argparse.FileType(\"w\", encoding=\"utf-8\"),\n        default=sys.stdout,\n        help=\"write report to filename\",\n    )\n    group = parser.add_mutually_exclusive_group(required=False)\n    group.add_argument(\n        \"-v\",\n        \"--verbose\",\n        dest=\"verbose\",\n        action=\"store_true\",\n        help=\"output extra information like excluded and included files\",\n    )\n    parser.add_argument(\n        \"-d\",\n        \"--debug\",\n        dest=\"debug\",\n        action=\"store_true\",\n        help=\"turn on debug mode\",\n    )\n    group.add_argument(\n        \"-q\",\n        \"--quiet\",\n        \"--silent\",\n        dest=\"quiet\",\n        action=\"store_true\",\n        help=\"only show output in the case of an error\",\n    )\n    parser.add_argument(\n        \"--ignore-nosec\",\n        dest=\"ignore_nosec\",\n        action=\"store_true\",\n        help=\"do not skip lines with # nosec comments\",\n    )\n    parser.add_argument(\n        \"-x\",\n        \"--exclude\",\n        dest=\"excluded_paths\",\n        action=\"store\",\n        default=\",\".join(constants.EXCLUDE),\n        help=\"comma-separated list of paths (glob patterns \"\n        \"supported) to exclude from scan \"\n        \"(note that these are in addition to the excluded \"\n        \"paths provided in the config file) (default: \"\n        + \",\".join(constants.EXCLUDE)\n        + \")\",\n    )\n    parser.add_argument(\n        \"-b\",\n        \"--baseline\",\n        dest=\"baseline\",\n        action=\"store\",\n        default=None,\n        help=\"path of a baseline report to compare against \"\n        \"(only JSON-formatted files are accepted)\",\n    )\n    parser.add_argument(\n        \"--ini\",\n        dest=\"ini_path\",\n        action=\"store\",\n        default=None,\n        help=\"path to a .bandit file that supplies command line arguments\",\n    )\n    parser.add_argument(\n        \"--exit-zero\",\n        action=\"store_true\",\n        dest=\"exit_zero\",\n        default=False,\n        help=\"exit with 0, \" \"even with results found\",\n    )\n    python_ver = sys.version.replace(\"\\n\", \"\")\n    parser.add_argument(\n        \"--version\",\n        action=\"version\",\n        version=f\"%(prog)s {bandit.__version__}\\n\"\n        f\"  python version = {python_ver}\",\n    )\n\n    parser.set_defaults(debug=False)\n    parser.set_defaults(verbose=False)\n    parser.set_defaults(quiet=False)\n    parser.set_defaults(ignore_nosec=False)\n\n    plugin_info = [\n        f\"{a[0]}\\t{a[1].name}\" for a in extension_mgr.plugins_by_id.items()\n    ]\n    blacklist_info = []\n    for a in extension_mgr.blacklist.items():\n        for b in a[1]:\n            blacklist_info.append(f\"{b['id']}\\t{b['name']}\")\n\n    plugin_list = \"\\n\\t\".join(sorted(set(plugin_info + blacklist_info)))\n    dedent_text = textwrap.dedent(\n        \"\"\"\n    CUSTOM FORMATTING\n    -----------------\n\n    Available tags:\n\n        {abspath}, {relpath}, {line}, {col}, {test_id},\n        {severity}, {msg}, {confidence}, {range}\n\n    Example usage:\n\n        Default template:\n        bandit -r examples/ --format custom --msg-template \\\\\n        \"{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}\"\n\n        Provides same output as:\n        bandit -r examples/ --format custom\n\n        Tags can also be formatted in python string.format() style:\n        bandit -r examples/ --format custom --msg-template \\\\\n        \"{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}\"\n\n        See python documentation for more information about formatting style:\n        https://docs.python.org/3/library/string.html\n\n    The following tests were discovered and loaded:\n    -----------------------------------------------\n    \"\"\"\n    )\n    parser.epilog = dedent_text + f\"\\t{plugin_list}\"\n\n    # setup work - parse arguments, and initialize BanditManager\n    args = parser.parse_args()\n    # Check if `--msg-template` is not present without custom formatter\n    if args.output_format != \"custom\" and args.msg_template is not None:\n        parser.error(\"--msg-template can only be used with --format=custom\")\n\n    # Check if confidence or severity level have been specified with strings\n    if args.severity_string is not None:\n        if args.severity_string == \"all\":\n            args.severity = 1\n        elif args.severity_string == \"low\":\n            args.severity = 2\n        elif args.severity_string == \"medium\":\n            args.severity = 3\n        elif args.severity_string == \"high\":\n            args.severity = 4\n        # Other strings will be blocked by argparse\n\n    if args.confidence_string is not None:\n        if args.confidence_string == \"all\":\n            args.confidence = 1\n        elif args.confidence_string == \"low\":\n            args.confidence = 2\n        elif args.confidence_string == \"medium\":\n            args.confidence = 3\n        elif args.confidence_string == \"high\":\n            args.confidence = 4\n        # Other strings will be blocked by argparse\n\n    # Handle .bandit files in projects to pass cmdline args from file\n    ini_options = _get_options_from_ini(args.ini_path, args.targets)\n    if ini_options:\n        # prefer command line, then ini file\n        args.config_file = _log_option_source(\n            parser.get_default(\"configfile\"),\n            args.config_file,\n            ini_options.get(\"configfile\"),\n            \"config file\",\n        )\n\n        args.excluded_paths = _log_option_source(\n            parser.get_default(\"excluded_paths\"),\n            args.excluded_paths,\n            ini_options.get(\"exclude\"),\n            \"excluded paths\",\n        )\n\n        args.skips = _log_option_source(\n            parser.get_default(\"skips\"),\n            args.skips,\n            ini_options.get(\"skips\"),\n            \"skipped tests\",\n        )\n\n        args.tests = _log_option_source(\n            parser.get_default(\"tests\"),\n            args.tests,\n            ini_options.get(\"tests\"),\n            \"selected tests\",\n        )\n\n        ini_targets = ini_options.get(\"targets\")\n        if ini_targets:\n            ini_targets = ini_targets.split(\",\")\n\n        args.targets = _log_option_source(\n            parser.get_default(\"targets\"),\n            args.targets,\n            ini_targets,\n            \"selected targets\",\n        )\n\n        # TODO(tmcpeak): any other useful options to pass from .bandit?\n\n        args.recursive = _log_option_source(\n            parser.get_default(\"recursive\"),\n            args.recursive,\n            ini_options.get(\"recursive\"),\n            \"recursive scan\",\n        )\n\n        args.agg_type = _log_option_source(\n            parser.get_default(\"agg_type\"),\n            args.agg_type,\n            ini_options.get(\"aggregate\"),\n            \"aggregate output type\",\n        )\n\n        args.context_lines = _log_option_source(\n            parser.get_default(\"context_lines\"),\n            args.context_lines,\n            int(ini_options.get(\"number\") or 0) or None,\n            \"max code lines output for issue\",\n        )\n\n        args.profile = _log_option_source(\n            parser.get_default(\"profile\"),\n            args.profile,\n            ini_options.get(\"profile\"),\n            \"profile\",\n        )\n\n        args.severity = _log_option_source(\n            parser.get_default(\"severity\"),\n            args.severity,\n            ini_options.get(\"level\"),\n            \"severity level\",\n        )\n\n        args.confidence = _log_option_source(\n            parser.get_default(\"confidence\"),\n            args.confidence,\n            ini_options.get(\"confidence\"),\n            \"confidence level\",\n        )\n\n        args.output_format = _log_option_source(\n            parser.get_default(\"output_format\"),\n            args.output_format,\n            ini_options.get(\"format\"),\n            \"output format\",\n        )\n\n        args.msg_template = _log_option_source(\n            parser.get_default(\"msg_template\"),\n            args.msg_template,\n            ini_options.get(\"msg-template\"),\n            \"output message template\",\n        )\n\n        args.output_file = _log_option_source(\n            parser.get_default(\"output_file\"),\n            args.output_file,\n            ini_options.get(\"output\"),\n            \"output file\",\n        )\n\n        args.verbose = _log_option_source(\n            parser.get_default(\"verbose\"),\n            args.verbose,\n            ini_options.get(\"verbose\"),\n            \"output extra information\",\n        )\n\n        args.debug = _log_option_source(\n            parser.get_default(\"debug\"),\n            args.debug,\n            ini_options.get(\"debug\"),\n            \"debug mode\",\n        )\n\n        args.quiet = _log_option_source(\n            parser.get_default(\"quiet\"),\n            args.quiet,\n            ini_options.get(\"quiet\"),\n            \"silent mode\",\n        )\n\n        args.ignore_nosec = _log_option_source(\n            parser.get_default(\"ignore_nosec\"),\n            args.ignore_nosec,\n            ini_options.get(\"ignore-nosec\"),\n            \"do not skip lines with # nosec\",\n        )\n\n        args.baseline = _log_option_source(\n            parser.get_default(\"baseline\"),\n            args.baseline,\n            ini_options.get(\"baseline\"),\n            \"path of a baseline report\",\n        )\n\n    try:\n        b_conf = b_config.BanditConfig(config_file=args.config_file)\n    except utils.ConfigError as e:\n        LOG.error(e)\n        sys.exit(2)\n\n    if not args.targets:\n        parser.print_usage()\n        sys.exit(2)\n\n    # if the log format string was set in the options, reinitialize\n    if b_conf.get_option(\"log_format\"):\n        log_format = b_conf.get_option(\"log_format\")\n        _init_logger(log_level=logging.DEBUG, log_format=log_format)\n\n    if args.quiet:\n        _init_logger(log_level=logging.WARN)\n\n    try:\n        profile = _get_profile(b_conf, args.profile, args.config_file)\n        _log_info(args, profile)\n\n        profile[\"include\"].update(args.tests.split(\",\") if args.tests else [])\n        profile[\"exclude\"].update(args.skips.split(\",\") if args.skips else [])\n        extension_mgr.validate_profile(profile)\n\n    except (utils.ProfileNotFound, ValueError) as e:\n        LOG.error(e)\n        sys.exit(2)\n\n    b_mgr = b_manager.BanditManager(\n        b_conf,\n        args.agg_type,\n        args.debug,\n        profile=profile,\n        verbose=args.verbose,\n        quiet=args.quiet,\n        ignore_nosec=args.ignore_nosec,\n    )\n\n    if args.baseline is not None:\n        try:\n            with open(args.baseline) as bl:\n                data = bl.read()\n                b_mgr.populate_baseline(data)\n        except OSError:\n            LOG.warning(\"Could not open baseline report: %s\", args.baseline)\n            sys.exit(2)\n\n        if args.output_format not in baseline_formatters:\n            LOG.warning(\n                \"Baseline must be used with one of the following \"\n                \"formats: \" + str(baseline_formatters)\n            )\n            sys.exit(2)\n\n    if args.output_format != \"json\":\n        if args.config_file:\n            LOG.info(\"using config: %s\", args.config_file)\n\n        LOG.info(\n            \"running on Python %d.%d.%d\",\n            sys.version_info.major,\n            sys.version_info.minor,\n            sys.version_info.micro,\n        )\n\n    # initiate file discovery step within Bandit Manager\n    b_mgr.discover_files(args.targets, args.recursive, args.excluded_paths)\n\n    if not b_mgr.b_ts.tests:\n        LOG.error(\"No tests would be run, please check the profile.\")\n        sys.exit(2)\n\n    # initiate execution of tests within Bandit Manager\n    b_mgr.run_tests()\n    LOG.debug(b_mgr.b_ma)\n    LOG.debug(b_mgr.metrics)\n\n    # trigger output of results by Bandit Manager\n    sev_level = constants.RANKING[args.severity - 1]\n    conf_level = constants.RANKING[args.confidence - 1]\n    b_mgr.output_results(\n        args.context_lines,\n        sev_level,\n        conf_level,\n        args.output_file,\n        args.output_format,\n        args.msg_template,\n    )\n\n    if (\n        b_mgr.results_count(sev_filter=sev_level, conf_filter=conf_level) > 0\n        and not args.exit_zero\n    ):\n        sys.exit(1)\n    else:\n        sys.exit(0)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "bandit/core/__init__.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nfrom bandit.core import config  # noqa\nfrom bandit.core import context  # noqa\nfrom bandit.core import manager  # noqa\nfrom bandit.core import meta_ast  # noqa\nfrom bandit.core import node_visitor  # noqa\nfrom bandit.core import test_set  # noqa\nfrom bandit.core import tester  # noqa\nfrom bandit.core import utils  # noqa\nfrom bandit.core.constants import *  # noqa\nfrom bandit.core.issue import *  # noqa\nfrom bandit.core.test_properties import *  # noqa\n"
  },
  {
    "path": "bandit/core/blacklisting.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\n\nfrom bandit.core import issue\n\n\ndef report_issue(check, name):\n    return issue.Issue(\n        severity=check.get(\"level\", \"MEDIUM\"),\n        confidence=\"HIGH\",\n        cwe=check.get(\"cwe\", issue.Cwe.NOTSET),\n        text=check[\"message\"].replace(\"{name}\", name),\n        ident=name,\n        test_id=check.get(\"id\", \"LEGACY\"),\n    )\n\n\ndef blacklist(context, config):\n    \"\"\"Generic blacklist test, B001.\n\n    This generic blacklist test will be called for any encountered node with\n    defined blacklist data available. This data is loaded via plugins using\n    the 'bandit.blacklists' entry point. Please see the documentation for more\n    details. Each blacklist datum has a unique bandit ID that may be used for\n    filtering purposes, or alternatively all blacklisting can be filtered using\n    the id of this built in test, 'B001'.\n    \"\"\"\n    blacklists = config\n    node_type = context.node.__class__.__name__\n\n    if node_type == \"Call\":\n        func = context.node.func\n        if isinstance(func, ast.Name) and func.id == \"__import__\":\n            if len(context.node.args):\n                if isinstance(\n                    context.node.args[0], ast.Constant\n                ) and isinstance(context.node.args[0].value, str):\n                    name = context.node.args[0].value\n                else:\n                    # TODO(??): import through a variable, need symbol tab\n                    name = \"UNKNOWN\"\n            else:\n                name = \"\"  # handle '__import__()'\n        else:\n            name = context.call_function_name_qual\n            # In the case the Call is an importlib.import, treat the first\n            # argument name as an actual import module name.\n            # Will produce None if argument is not a literal or identifier\n            if name in [\"importlib.import_module\", \"importlib.__import__\"]:\n                if context.call_args_count > 0:\n                    name = context.call_args[0]\n                else:\n                    name = context.call_keywords[\"name\"]\n        for check in blacklists[node_type]:\n            for qn in check[\"qualnames\"]:\n                if name is not None and name == qn:\n                    return report_issue(check, name)\n\n    if node_type.startswith(\"Import\"):\n        prefix = \"\"\n        if node_type == \"ImportFrom\":\n            if context.node.module is not None:\n                prefix = context.node.module + \".\"\n\n        for check in blacklists[node_type]:\n            for name in context.node.names:\n                for qn in check[\"qualnames\"]:\n                    if (prefix + name.name).startswith(qn):\n                        return report_issue(check, name.name)\n"
  },
  {
    "path": "bandit/core/config.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport logging\nimport sys\n\nimport yaml\n\nif sys.version_info >= (3, 11):\n    import tomllib\nelse:\n    try:\n        import tomli as tomllib\n    except ImportError:\n        tomllib = None\n\nfrom bandit.core import constants\nfrom bandit.core import extension_loader\nfrom bandit.core import utils\n\nLOG = logging.getLogger(__name__)\n\n\nclass BanditConfig:\n    def __init__(self, config_file=None):\n        \"\"\"Attempt to initialize a config dictionary from a yaml file.\n\n        Error out if loading the yaml file fails for any reason.\n        :param config_file: The Bandit yaml config file\n\n        :raises bandit.utils.ConfigError: If the config is invalid or\n            unreadable.\n        \"\"\"\n        self.config_file = config_file\n        self._config = {}\n\n        if config_file:\n            try:\n                f = open(config_file, \"rb\")\n            except OSError:\n                raise utils.ConfigError(\n                    \"Could not read config file.\", config_file\n                )\n\n            if config_file.endswith(\".toml\"):\n                if tomllib is None:\n                    raise utils.ConfigError(\n                        \"toml parser not available, reinstall with toml extra\",\n                        config_file,\n                    )\n\n                try:\n                    with f:\n                        self._config = (\n                            tomllib.load(f).get(\"tool\", {}).get(\"bandit\", {})\n                        )\n                except tomllib.TOMLDecodeError as err:\n                    LOG.error(err)\n                    raise utils.ConfigError(\"Error parsing file.\", config_file)\n            else:\n                try:\n                    with f:\n                        self._config = yaml.safe_load(f)\n                except yaml.YAMLError as err:\n                    LOG.error(err)\n                    raise utils.ConfigError(\"Error parsing file.\", config_file)\n\n            self.validate(config_file)\n\n            # valid config must be a dict\n            if not isinstance(self._config, dict):\n                raise utils.ConfigError(\"Error parsing file.\", config_file)\n\n            self.convert_legacy_config()\n\n        else:\n            # use sane defaults\n            self._config[\"plugin_name_pattern\"] = \"*.py\"\n            self._config[\"include\"] = [\"*.py\", \"*.pyw\"]\n\n        self._init_settings()\n\n    def get_option(self, option_string):\n        \"\"\"Returns the option from the config specified by the option_string.\n\n        '.' can be used to denote levels, for example to retrieve the options\n        from the 'a' profile you can use 'profiles.a'\n        :param option_string: The string specifying the option to retrieve\n        :return: The object specified by the option_string, or None if it can't\n        be found.\n        \"\"\"\n        option_levels = option_string.split(\".\")\n        cur_item = self._config\n        for level in option_levels:\n            if cur_item and (level in cur_item):\n                cur_item = cur_item[level]\n            else:\n                return None\n\n        return cur_item\n\n    def get_setting(self, setting_name):\n        if setting_name in self._settings:\n            return self._settings[setting_name]\n        else:\n            return None\n\n    @property\n    def config(self):\n        \"\"\"Property to return the config dictionary\n\n        :return: Config dictionary\n        \"\"\"\n        return self._config\n\n    def _init_settings(self):\n        \"\"\"This function calls a set of other functions (one per setting)\n\n        This function calls a set of other functions (one per setting) to build\n        out the _settings dictionary.  Each other function will set values from\n        the config (if set), otherwise use defaults (from constants if\n        possible).\n        :return: -\n        \"\"\"\n        self._settings = {}\n        self._init_plugin_name_pattern()\n\n    def _init_plugin_name_pattern(self):\n        \"\"\"Sets settings['plugin_name_pattern'] from default or config file.\"\"\"\n        plugin_name_pattern = constants.plugin_name_pattern\n        if self.get_option(\"plugin_name_pattern\"):\n            plugin_name_pattern = self.get_option(\"plugin_name_pattern\")\n        self._settings[\"plugin_name_pattern\"] = plugin_name_pattern\n\n    def convert_legacy_config(self):\n        updated_profiles = self.convert_names_to_ids()\n        bad_calls, bad_imports = self.convert_legacy_blacklist_data()\n\n        if updated_profiles:\n            self.convert_legacy_blacklist_tests(\n                updated_profiles, bad_calls, bad_imports\n            )\n            self._config[\"profiles\"] = updated_profiles\n\n    def convert_names_to_ids(self):\n        \"\"\"Convert test names to IDs, unknown names are left unchanged.\"\"\"\n        extman = extension_loader.MANAGER\n\n        updated_profiles = {}\n        for name, profile in (self.get_option(\"profiles\") or {}).items():\n            # NOTE(tkelsey): can't use default of get() because value is\n            # sometimes explicitly 'None', for example when the list is given\n            # in yaml but not populated with any values.\n            include = {\n                (extman.get_test_id(i) or i)\n                for i in (profile.get(\"include\") or [])\n            }\n            exclude = {\n                (extman.get_test_id(i) or i)\n                for i in (profile.get(\"exclude\") or [])\n            }\n            updated_profiles[name] = {\"include\": include, \"exclude\": exclude}\n        return updated_profiles\n\n    def convert_legacy_blacklist_data(self):\n        \"\"\"Detect legacy blacklist data and convert it to new format.\"\"\"\n        bad_calls_list = []\n        bad_imports_list = []\n\n        bad_calls = self.get_option(\"blacklist_calls\") or {}\n        bad_calls = bad_calls.get(\"bad_name_sets\", {})\n        for item in bad_calls:\n            for key, val in item.items():\n                val[\"name\"] = key\n                val[\"message\"] = val[\"message\"].replace(\"{func}\", \"{name}\")\n                bad_calls_list.append(val)\n\n        bad_imports = self.get_option(\"blacklist_imports\") or {}\n        bad_imports = bad_imports.get(\"bad_import_sets\", {})\n        for item in bad_imports:\n            for key, val in item.items():\n                val[\"name\"] = key\n                val[\"message\"] = val[\"message\"].replace(\"{module}\", \"{name}\")\n                val[\"qualnames\"] = val[\"imports\"]\n                del val[\"imports\"]\n                bad_imports_list.append(val)\n\n        if bad_imports_list or bad_calls_list:\n            LOG.warning(\n                \"Legacy blacklist data found in config, overriding \"\n                \"data plugins\"\n            )\n        return bad_calls_list, bad_imports_list\n\n    @staticmethod\n    def convert_legacy_blacklist_tests(profiles, bad_imports, bad_calls):\n        \"\"\"Detect old blacklist tests, convert to use new builtin.\"\"\"\n\n        def _clean_set(name, data):\n            if name in data:\n                data.remove(name)\n                data.add(\"B001\")\n\n        for name, profile in profiles.items():\n            blacklist = {}\n            include = profile[\"include\"]\n            exclude = profile[\"exclude\"]\n\n            name = \"blacklist_calls\"\n            if name in include and name not in exclude:\n                blacklist.setdefault(\"Call\", []).extend(bad_calls)\n\n            _clean_set(name, include)\n            _clean_set(name, exclude)\n\n            name = \"blacklist_imports\"\n            if name in include and name not in exclude:\n                blacklist.setdefault(\"Import\", []).extend(bad_imports)\n                blacklist.setdefault(\"ImportFrom\", []).extend(bad_imports)\n                blacklist.setdefault(\"Call\", []).extend(bad_imports)\n\n            _clean_set(name, include)\n            _clean_set(name, exclude)\n            _clean_set(\"blacklist_import_func\", include)\n            _clean_set(\"blacklist_import_func\", exclude)\n\n            # This can happen with a legacy config that includes\n            # blacklist_calls but exclude blacklist_imports for example\n            if \"B001\" in include and \"B001\" in exclude:\n                exclude.remove(\"B001\")\n\n            profile[\"blacklist\"] = blacklist\n\n    def validate(self, path):\n        \"\"\"Validate the config data.\"\"\"\n        legacy = False\n        message = (\n            \"Config file has an include or exclude reference \"\n            \"to legacy test '{0}' but no configuration data for \"\n            \"it. Configuration data is required for this test. \"\n            \"Please consider switching to the new config file \"\n            \"format, the tool 'bandit-config-generator' can help \"\n            \"you with this.\"\n        )\n\n        def _test(key, block, exclude, include):\n            if key in exclude or key in include:\n                if self._config.get(block) is None:\n                    raise utils.ConfigError(message.format(key), path)\n\n        if \"profiles\" in self._config:\n            legacy = True\n            for profile in self._config[\"profiles\"].values():\n                inc = profile.get(\"include\") or set()\n                exc = profile.get(\"exclude\") or set()\n\n                _test(\"blacklist_imports\", \"blacklist_imports\", inc, exc)\n                _test(\"blacklist_import_func\", \"blacklist_imports\", inc, exc)\n                _test(\"blacklist_calls\", \"blacklist_calls\", inc, exc)\n\n        # show deprecation message\n        if legacy:\n            LOG.warning(\n                \"Config file '%s' contains deprecated legacy config \"\n                \"data. Please consider upgrading to the new config \"\n                \"format. The tool 'bandit-config-generator' can help \"\n                \"you with this. Support for legacy configs will be \"\n                \"removed in a future bandit version.\",\n                path,\n            )\n"
  },
  {
    "path": "bandit/core/constants.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\n# default plugin name pattern\nplugin_name_pattern = \"*.py\"\n\nRANKING = [\"UNDEFINED\", \"LOW\", \"MEDIUM\", \"HIGH\"]\nRANKING_VALUES = {\"UNDEFINED\": 1, \"LOW\": 3, \"MEDIUM\": 5, \"HIGH\": 10}\nCRITERIA = [(\"SEVERITY\", \"UNDEFINED\"), (\"CONFIDENCE\", \"UNDEFINED\")]\n\n# add each ranking to globals, to allow direct access in module name space\nfor rank in RANKING:\n    globals()[rank] = rank\n\nCONFIDENCE_DEFAULT = \"UNDEFINED\"\n\n# A list of values Python considers to be False.\n# These can be useful in tests to check if a value is True or False.\n# We don't handle the case of user-defined classes being false.\n# These are only useful when we have a constant in code. If we\n# have a variable we cannot determine if False.\n# See https://docs.python.org/3/library/stdtypes.html#truth-value-testing\nFALSE_VALUES = [None, False, \"False\", 0, 0.0, 0j, \"\", (), [], {}]\n\n# override with \"log_format\" option in config file\nlog_format_string = \"[%(module)s]\\t%(levelname)s\\t%(message)s\"\n\n# Directories to exclude by default\nEXCLUDE = (\n    \".svn\",\n    \"CVS\",\n    \".bzr\",\n    \".hg\",\n    \".git\",\n    \"__pycache__\",\n    \".tox\",\n    \".eggs\",\n    \"*.egg\",\n)\n"
  },
  {
    "path": "bandit/core/context.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\n\nfrom bandit.core import utils\n\n\nclass Context:\n    def __init__(self, context_object=None):\n        \"\"\"Initialize the class with a context, empty dict otherwise\n\n        :param context_object: The context object to create class from\n        :return: -\n        \"\"\"\n        if context_object is not None:\n            self._context = context_object\n        else:\n            self._context = dict()\n\n    def __repr__(self):\n        \"\"\"Generate representation of object for printing / interactive use\n\n        Most likely only interested in non-default properties, so we return\n        the string version of _context.\n\n        Example string returned:\n        <Context {'node': <_ast.Call object at 0x110252510>, 'function': None,\n        'name': 'socket', 'imports': set(['socket']), 'module': None,\n        'filename': 'examples/binding.py',\n        'call': <_ast.Call object at 0x110252510>, 'lineno': 3,\n        'import_aliases': {}, 'qualname': 'socket.socket'}>\n\n        :return: A string representation of the object\n        \"\"\"\n        return f\"<Context {self._context}>\"\n\n    @property\n    def call_args(self):\n        \"\"\"Get a list of function args\n\n        :return: A list of function args\n        \"\"\"\n        args = []\n        if \"call\" in self._context and hasattr(self._context[\"call\"], \"args\"):\n            for arg in self._context[\"call\"].args:\n                if hasattr(arg, \"attr\"):\n                    args.append(arg.attr)\n                else:\n                    args.append(self._get_literal_value(arg))\n        return args\n\n    @property\n    def call_args_count(self):\n        \"\"\"Get the number of args a function call has\n\n        :return: The number of args a function call has or None\n        \"\"\"\n        if \"call\" in self._context and hasattr(self._context[\"call\"], \"args\"):\n            return len(self._context[\"call\"].args)\n        else:\n            return None\n\n    @property\n    def call_function_name(self):\n        \"\"\"Get the name (not FQ) of a function call\n\n        :return: The name (not FQ) of a function call\n        \"\"\"\n        return self._context.get(\"name\")\n\n    @property\n    def call_function_name_qual(self):\n        \"\"\"Get the FQ name of a function call\n\n        :return: The FQ name of a function call\n        \"\"\"\n        return self._context.get(\"qualname\")\n\n    @property\n    def call_keywords(self):\n        \"\"\"Get a dictionary of keyword parameters\n\n        :return: A dictionary of keyword parameters for a call as strings\n        \"\"\"\n        if \"call\" in self._context and hasattr(\n            self._context[\"call\"], \"keywords\"\n        ):\n            return_dict = {}\n            for li in self._context[\"call\"].keywords:\n                if hasattr(li.value, \"attr\"):\n                    return_dict[li.arg] = li.value.attr\n                else:\n                    return_dict[li.arg] = self._get_literal_value(li.value)\n            return return_dict\n        else:\n            return None\n\n    @property\n    def node(self):\n        \"\"\"Get the raw AST node associated with the context\n\n        :return: The raw AST node associated with the context\n        \"\"\"\n        return self._context.get(\"node\")\n\n    @property\n    def string_val(self):\n        \"\"\"Get the value of a standalone unicode or string object\n\n        :return: value of a standalone unicode or string object\n        \"\"\"\n        return self._context.get(\"str\")\n\n    @property\n    def bytes_val(self):\n        \"\"\"Get the value of a standalone bytes object (py3 only)\n\n        :return: value of a standalone bytes object\n        \"\"\"\n        return self._context.get(\"bytes\")\n\n    @property\n    def string_val_as_escaped_bytes(self):\n        \"\"\"Get escaped value of the object.\n\n        Turn the value of a string or bytes object into byte sequence with\n        unknown, control, and \\\\ characters escaped.\n\n        This function should be used when looking for a known sequence in a\n        potentially badly encoded string in the code.\n\n        :return: sequence of printable ascii bytes representing original string\n        \"\"\"\n        val = self.string_val\n        if val is not None:\n            # it's any of str or unicode in py2, or str in py3\n            return val.encode(\"unicode_escape\")\n\n        val = self.bytes_val\n        if val is not None:\n            return utils.escaped_bytes_representation(val)\n\n        return None\n\n    @property\n    def statement(self):\n        \"\"\"Get the raw AST for the current statement\n\n        :return: The raw AST for the current statement\n        \"\"\"\n        return self._context.get(\"statement\")\n\n    @property\n    def function_def_defaults_qual(self):\n        \"\"\"Get a list of fully qualified default values in a function def\n\n        :return: List of defaults\n        \"\"\"\n        defaults = []\n        if (\n            \"node\" in self._context\n            and hasattr(self._context[\"node\"], \"args\")\n            and hasattr(self._context[\"node\"].args, \"defaults\")\n        ):\n            for default in self._context[\"node\"].args.defaults:\n                defaults.append(\n                    utils.get_qual_attr(\n                        default, self._context[\"import_aliases\"]\n                    )\n                )\n        return defaults\n\n    def _get_literal_value(self, literal):\n        \"\"\"Utility function to turn AST literals into native Python types\n\n        :param literal: The AST literal to convert\n        :return: The value of the AST literal\n        \"\"\"\n        if isinstance(literal, ast.Constant):\n            if isinstance(literal.value, bool):\n                literal_value = str(literal.value)\n            elif literal.value is None:\n                literal_value = str(literal.value)\n            else:\n                literal_value = literal.value\n\n        elif isinstance(literal, ast.List):\n            return_list = list()\n            for li in literal.elts:\n                return_list.append(self._get_literal_value(li))\n            literal_value = return_list\n\n        elif isinstance(literal, ast.Tuple):\n            return_tuple = tuple()\n            for ti in literal.elts:\n                return_tuple += (self._get_literal_value(ti),)\n            literal_value = return_tuple\n\n        elif isinstance(literal, ast.Set):\n            return_set = set()\n            for si in literal.elts:\n                return_set.add(self._get_literal_value(si))\n            literal_value = return_set\n\n        elif isinstance(literal, ast.Dict):\n            literal_value = dict(zip(literal.keys, literal.values))\n\n        elif isinstance(literal, ast.Name):\n            literal_value = literal.id\n\n        else:\n            literal_value = None\n\n        return literal_value\n\n    def get_call_arg_value(self, argument_name):\n        \"\"\"Gets the value of a named argument in a function call.\n\n        :return: named argument value\n        \"\"\"\n        kwd_values = self.call_keywords\n        if kwd_values is not None and argument_name in kwd_values:\n            return kwd_values[argument_name]\n\n    def check_call_arg_value(self, argument_name, argument_values=None):\n        \"\"\"Checks for a value of a named argument in a function call.\n\n        Returns none if the specified argument is not found.\n        :param argument_name: A string - name of the argument to look for\n        :param argument_values: the value, or list of values to test against\n        :return: Boolean True if argument found and matched, False if\n        found and not matched, None if argument not found at all\n        \"\"\"\n        arg_value = self.get_call_arg_value(argument_name)\n        if arg_value is not None:\n            if not isinstance(argument_values, list):\n                # if passed a single value, or a tuple, convert to a list\n                argument_values = list((argument_values,))\n            for val in argument_values:\n                if arg_value == val:\n                    return True\n            return False\n        else:\n            # argument name not found, return None to allow testing for this\n            # eventuality\n            return None\n\n    def get_lineno_for_call_arg(self, argument_name):\n        \"\"\"Get the line number for a specific named argument\n\n        In case the call is split over multiple lines, get the correct one for\n        the argument.\n        :param argument_name: A string - name of the argument to look for\n        :return: Integer - the line number of the found argument, or -1\n        \"\"\"\n        if hasattr(self.node, \"keywords\"):\n            for key in self.node.keywords:\n                if key.arg == argument_name:\n                    return key.value.lineno\n\n    def get_call_arg_at_position(self, position_num):\n        \"\"\"Returns positional argument at the specified position (if it exists)\n\n        :param position_num: The index of the argument to return the value for\n        :return: Value of the argument at the specified position if it exists\n        \"\"\"\n        max_args = self.call_args_count\n        if max_args and position_num < max_args:\n            arg = self._context[\"call\"].args[position_num]\n            return getattr(arg, \"attr\", None) or self._get_literal_value(arg)\n        else:\n            return None\n\n    def is_module_being_imported(self, module):\n        \"\"\"Check for the specified module is currently being imported\n\n        :param module: The module name to look for\n        :return: True if the module is found, False otherwise\n        \"\"\"\n        return self._context.get(\"module\") == module\n\n    def is_module_imported_exact(self, module):\n        \"\"\"Check if a specified module has been imported; only exact matches.\n\n        :param module: The module name to look for\n        :return: True if the module is found, False otherwise\n        \"\"\"\n        return module in self._context.get(\"imports\", [])\n\n    def is_module_imported_like(self, module):\n        \"\"\"Check if a specified module has been imported\n\n        Check if a specified module has been imported; specified module exists\n        as part of any import statement.\n        :param module: The module name to look for\n        :return: True if the module is found, False otherwise\n        \"\"\"\n        if \"imports\" in self._context:\n            for imp in self._context[\"imports\"]:\n                if module in imp:\n                    return True\n        return False\n\n    @property\n    def filename(self):\n        return self._context.get(\"filename\")\n\n    @property\n    def file_data(self):\n        return self._context.get(\"file_data\")\n\n    @property\n    def import_aliases(self):\n        return self._context.get(\"import_aliases\")\n"
  },
  {
    "path": "bandit/core/docs_utils.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport bandit\n\n\ndef get_url(bid):\n    # where our docs are hosted\n    base_url = f\"https://bandit.readthedocs.io/en/{bandit.__version__}/\"\n\n    # NOTE(tkelsey): for some reason this import can't be found when stevedore\n    # loads up the formatter plugin that imports this file. It is available\n    # later though.\n    from bandit.core import extension_loader\n\n    info = extension_loader.MANAGER.plugins_by_id.get(bid)\n    if info is not None:\n        return f\"{base_url}plugins/{bid.lower()}_{info.plugin.__name__}.html\"\n\n    info = extension_loader.MANAGER.blacklist_by_id.get(bid)\n    if info is not None:\n        template = \"blacklists/blacklist_{kind}.html#{id}-{name}\"\n        info[\"name\"] = info[\"name\"].replace(\"_\", \"-\")\n\n        if info[\"id\"].startswith(\"B3\"):  # B3XX\n            # Some of the links are combined, so we have exception cases\n            if info[\"id\"] in [\"B304\", \"B305\"]:\n                info = info.copy()\n                info[\"id\"] = \"b304-b305\"\n                info[\"name\"] = \"ciphers-and-modes\"\n            elif info[\"id\"] in [\n                \"B313\",\n                \"B314\",\n                \"B315\",\n                \"B316\",\n                \"B317\",\n                \"B318\",\n                \"B319\",\n                \"B320\",\n            ]:\n                info = info.copy()\n                info[\"id\"] = \"b313-b320\"\n            ext = template.format(\n                kind=\"calls\", id=info[\"id\"], name=info[\"name\"]\n            )\n        else:\n            ext = template.format(\n                kind=\"imports\", id=info[\"id\"], name=info[\"name\"]\n            )\n\n        return base_url + ext.lower()\n\n    return base_url  # no idea, give the docs main page\n"
  },
  {
    "path": "bandit/core/extension_loader.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nimport logging\nimport sys\n\nfrom stevedore import extension\n\nfrom bandit.core import utils\n\nLOG = logging.getLogger(__name__)\n\n\nclass Manager:\n    # These IDs are for bandit built in tests\n    builtin = [\"B001\"]  # Built in blacklist test\n\n    def __init__(\n        self,\n        formatters_namespace=\"bandit.formatters\",\n        plugins_namespace=\"bandit.plugins\",\n        blacklists_namespace=\"bandit.blacklists\",\n    ):\n        # Cache the extension managers, loaded extensions, and extension names\n        self.load_formatters(formatters_namespace)\n        self.load_plugins(plugins_namespace)\n        self.load_blacklists(blacklists_namespace)\n\n    def load_formatters(self, formatters_namespace):\n        self.formatters_mgr = extension.ExtensionManager(\n            namespace=formatters_namespace,\n            invoke_on_load=False,\n            verify_requirements=False,\n        )\n        self.formatters = list(self.formatters_mgr)\n        self.formatter_names = self.formatters_mgr.names()\n\n    def load_plugins(self, plugins_namespace):\n        self.plugins_mgr = extension.ExtensionManager(\n            namespace=plugins_namespace,\n            invoke_on_load=False,\n            verify_requirements=False,\n        )\n\n        def test_has_id(plugin):\n            if not hasattr(plugin.plugin, \"_test_id\"):\n                # logger not setup yet, so using print\n                print(\n                    f\"WARNING: Test '{plugin.name}' has no ID, skipping.\",\n                    file=sys.stderr,\n                )\n                return False\n            return True\n\n        self.plugins = list(filter(test_has_id, list(self.plugins_mgr)))\n        self.plugin_names = [plugin.name for plugin in self.plugins]\n        self.plugins_by_id = {p.plugin._test_id: p for p in self.plugins}\n        self.plugins_by_name = {p.name: p for p in self.plugins}\n\n    def get_test_id(self, test_name):\n        if test_name in self.plugins_by_name:\n            return self.plugins_by_name[test_name].plugin._test_id\n        if test_name in self.blacklist_by_name:\n            return self.blacklist_by_name[test_name][\"id\"]\n        return None\n\n    def load_blacklists(self, blacklist_namespace):\n        self.blacklists_mgr = extension.ExtensionManager(\n            namespace=blacklist_namespace,\n            invoke_on_load=False,\n            verify_requirements=False,\n        )\n        self.blacklist = {}\n        blacklist = list(self.blacklists_mgr)\n        for item in blacklist:\n            for key, val in item.plugin().items():\n                utils.check_ast_node(key)\n                self.blacklist.setdefault(key, []).extend(val)\n\n        self.blacklist_by_id = {}\n        self.blacklist_by_name = {}\n        for val in self.blacklist.values():\n            for b in val:\n                self.blacklist_by_id[b[\"id\"]] = b\n                self.blacklist_by_name[b[\"name\"]] = b\n\n    def validate_profile(self, profile):\n        \"\"\"Validate that everything in the configured profiles looks good.\"\"\"\n        for inc in profile[\"include\"]:\n            if not self.check_id(inc):\n                LOG.warning(f\"Unknown test found in profile: {inc}\")\n\n        for exc in profile[\"exclude\"]:\n            if not self.check_id(exc):\n                LOG.warning(f\"Unknown test found in profile: {exc}\")\n\n        union = set(profile[\"include\"]) & set(profile[\"exclude\"])\n        if len(union) > 0:\n            raise ValueError(\n                f\"Non-exclusive include/exclude test sets: {union}\"\n            )\n\n    def check_id(self, test):\n        return (\n            test in self.plugins_by_id\n            or test in self.blacklist_by_id\n            or test in self.builtin\n        )\n\n\n# Using entry-points and pkg_resources *can* be expensive. So let's load these\n# once, store them on the object, and have a module global object for\n# accessing them. After the first time this module is imported, it should save\n# this attribute on the module and not have to reload the entry-points.\nMANAGER = Manager()\n"
  },
  {
    "path": "bandit/core/issue.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport linecache\n\nfrom bandit.core import constants\n\n\nclass Cwe:\n    NOTSET = 0\n    IMPROPER_INPUT_VALIDATION = 20\n    PATH_TRAVERSAL = 22\n    OS_COMMAND_INJECTION = 78\n    XSS = 79\n    BASIC_XSS = 80\n    SQL_INJECTION = 89\n    CODE_INJECTION = 94\n    IMPROPER_WILDCARD_NEUTRALIZATION = 155\n    HARD_CODED_PASSWORD = 259\n    IMPROPER_ACCESS_CONTROL = 284\n    IMPROPER_CERT_VALIDATION = 295\n    CLEARTEXT_TRANSMISSION = 319\n    INADEQUATE_ENCRYPTION_STRENGTH = 326\n    BROKEN_CRYPTO = 327\n    INSUFFICIENT_RANDOM_VALUES = 330\n    INSECURE_TEMP_FILE = 377\n    UNCONTROLLED_RESOURCE_CONSUMPTION = 400\n    DOWNLOAD_OF_CODE_WITHOUT_INTEGRITY_CHECK = 494\n    DESERIALIZATION_OF_UNTRUSTED_DATA = 502\n    MULTIPLE_BINDS = 605\n    IMPROPER_CHECK_OF_EXCEPT_COND = 703\n    INCORRECT_PERMISSION_ASSIGNMENT = 732\n    INAPPROPRIATE_ENCODING_FOR_OUTPUT_CONTEXT = 838\n\n    MITRE_URL_PATTERN = \"https://cwe.mitre.org/data/definitions/%s.html\"\n\n    def __init__(self, id=NOTSET):\n        self.id = id\n\n    def link(self):\n        if self.id == Cwe.NOTSET:\n            return \"\"\n\n        return Cwe.MITRE_URL_PATTERN % str(self.id)\n\n    def __str__(self):\n        if self.id == Cwe.NOTSET:\n            return \"\"\n\n        return \"CWE-%i (%s)\" % (self.id, self.link())\n\n    def as_dict(self):\n        return (\n            {\"id\": self.id, \"link\": self.link()}\n            if self.id != Cwe.NOTSET\n            else {}\n        )\n\n    def as_jsons(self):\n        return str(self.as_dict())\n\n    def from_dict(self, data):\n        if \"id\" in data:\n            self.id = int(data[\"id\"])\n        else:\n            self.id = Cwe.NOTSET\n\n    def __eq__(self, other):\n        return self.id == other.id\n\n    def __ne__(self, other):\n        return self.id != other.id\n\n    def __hash__(self):\n        return id(self)\n\n\nclass Issue:\n    def __init__(\n        self,\n        severity,\n        cwe=0,\n        confidence=constants.CONFIDENCE_DEFAULT,\n        text=\"\",\n        ident=None,\n        lineno=None,\n        test_id=\"\",\n        col_offset=-1,\n        end_col_offset=0,\n    ):\n        self.severity = severity\n        self.cwe = Cwe(cwe)\n        self.confidence = confidence\n        if isinstance(text, bytes):\n            text = text.decode(\"utf-8\")\n        self.text = text\n        self.ident = ident\n        self.fname = \"\"\n        self.fdata = None\n        self.test = \"\"\n        self.test_id = test_id\n        self.lineno = lineno\n        self.col_offset = col_offset\n        self.end_col_offset = end_col_offset\n        self.linerange = []\n\n    def __str__(self):\n        return (\n            \"Issue: '%s' from %s:%s: CWE: %s, Severity: %s Confidence: \"\n            \"%s at %s:%i:%i\"\n        ) % (\n            self.text,\n            self.test_id,\n            (self.ident or self.test),\n            str(self.cwe),\n            self.severity,\n            self.confidence,\n            self.fname,\n            self.lineno,\n            self.col_offset,\n        )\n\n    def __eq__(self, other):\n        # if the issue text, severity, confidence, and filename match, it's\n        # the same issue from our perspective\n        match_types = [\n            \"text\",\n            \"severity\",\n            \"cwe\",\n            \"confidence\",\n            \"fname\",\n            \"test\",\n            \"test_id\",\n        ]\n        return all(\n            getattr(self, field) == getattr(other, field)\n            for field in match_types\n        )\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n    def __hash__(self):\n        return id(self)\n\n    def filter(self, severity, confidence):\n        \"\"\"Utility to filter on confidence and severity\n\n        This function determines whether an issue should be included by\n        comparing the severity and confidence rating of the issue to minimum\n        thresholds specified in 'severity' and 'confidence' respectively.\n\n        Formatters should call manager.filter_results() directly.\n\n        This will return false if either the confidence or severity of the\n        issue are lower than the given threshold values.\n\n        :param severity: Severity threshold\n        :param confidence: Confidence threshold\n        :return: True/False depending on whether issue meets threshold\n\n        \"\"\"\n        rank = constants.RANKING\n        return rank.index(self.severity) >= rank.index(\n            severity\n        ) and rank.index(self.confidence) >= rank.index(confidence)\n\n    def get_code(self, max_lines=3, tabbed=False):\n        \"\"\"Gets lines of code from a file the generated this issue.\n\n        :param max_lines: Max lines of context to return\n        :param tabbed: Use tabbing in the output\n        :return: strings of code\n        \"\"\"\n        lines = []\n        max_lines = max(max_lines, 1)\n        lmin = max(1, self.lineno - max_lines // 2)\n        lmax = lmin + len(self.linerange) + max_lines - 1\n\n        if self.fname == \"<stdin>\":\n            self.fdata.seek(0)\n            for line_num in range(1, lmin):\n                self.fdata.readline()\n\n        tmplt = \"%i\\t%s\" if tabbed else \"%i %s\"\n        for line in range(lmin, lmax):\n            if self.fname == \"<stdin>\":\n                text = self.fdata.readline()\n            else:\n                text = linecache.getline(self.fname, line)\n\n            if isinstance(text, bytes):\n                text = text.decode(\"utf-8\")\n\n            if not len(text):\n                break\n            lines.append(tmplt % (line, text))\n        return \"\".join(lines)\n\n    def as_dict(self, with_code=True, max_lines=3):\n        \"\"\"Convert the issue to a dict of values for outputting.\"\"\"\n        out = {\n            \"filename\": self.fname,\n            \"test_name\": self.test,\n            \"test_id\": self.test_id,\n            \"issue_severity\": self.severity,\n            \"issue_cwe\": self.cwe.as_dict(),\n            \"issue_confidence\": self.confidence,\n            \"issue_text\": self.text.encode(\"utf-8\").decode(\"utf-8\"),\n            \"line_number\": self.lineno,\n            \"line_range\": self.linerange,\n            \"col_offset\": self.col_offset,\n            \"end_col_offset\": self.end_col_offset,\n        }\n\n        if with_code:\n            out[\"code\"] = self.get_code(max_lines=max_lines)\n        return out\n\n    def from_dict(self, data, with_code=True):\n        self.code = data[\"code\"]\n        self.fname = data[\"filename\"]\n        self.severity = data[\"issue_severity\"]\n        self.cwe = cwe_from_dict(data[\"issue_cwe\"])\n        self.confidence = data[\"issue_confidence\"]\n        self.text = data[\"issue_text\"]\n        self.test = data[\"test_name\"]\n        self.test_id = data[\"test_id\"]\n        self.lineno = data[\"line_number\"]\n        self.linerange = data[\"line_range\"]\n        self.col_offset = data.get(\"col_offset\", 0)\n        self.end_col_offset = data.get(\"end_col_offset\", 0)\n\n\ndef cwe_from_dict(data):\n    cwe = Cwe()\n    cwe.from_dict(data)\n    return cwe\n\n\ndef issue_from_dict(data):\n    i = Issue(severity=data[\"issue_severity\"])\n    i.from_dict(data)\n    return i\n"
  },
  {
    "path": "bandit/core/manager.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport fnmatch\nimport io\nimport json\nimport logging\nimport os\nimport re\nimport sys\nimport tokenize\nimport traceback\n\nfrom rich import progress\n\nfrom bandit.core import constants as b_constants\nfrom bandit.core import extension_loader\nfrom bandit.core import issue\nfrom bandit.core import meta_ast as b_meta_ast\nfrom bandit.core import metrics\nfrom bandit.core import node_visitor as b_node_visitor\nfrom bandit.core import test_set as b_test_set\n\nLOG = logging.getLogger(__name__)\nNOSEC_COMMENT = re.compile(r\"#\\s*nosec:?\\s*(?P<tests>[^#]+)?#?\")\nNOSEC_COMMENT_TESTS = re.compile(r\"(?:(B\\d+|[a-z\\d_]+),?)+\", re.IGNORECASE)\nPROGRESS_THRESHOLD = 50\n\n\nclass BanditManager:\n    scope = []\n\n    def __init__(\n        self,\n        config,\n        agg_type,\n        debug=False,\n        verbose=False,\n        quiet=False,\n        profile=None,\n        ignore_nosec=False,\n    ):\n        \"\"\"Get logger, config, AST handler, and result store ready\n\n        :param config: config options object\n        :type config: bandit.core.BanditConfig\n        :param agg_type: aggregation type\n        :param debug: Whether to show debug messages or not\n        :param verbose: Whether to show verbose output\n        :param quiet: Whether to only show output in the case of an error\n        :param profile_name: Optional name of profile to use (from cmd line)\n        :param ignore_nosec: Whether to ignore #nosec or not\n        :return:\n        \"\"\"\n        self.debug = debug\n        self.verbose = verbose\n        self.quiet = quiet\n        if not profile:\n            profile = {}\n        self.ignore_nosec = ignore_nosec\n        self.b_conf = config\n        self.files_list = []\n        self.excluded_files = []\n        self.b_ma = b_meta_ast.BanditMetaAst()\n        self.skipped = []\n        self.results = []\n        self.baseline = []\n        self.agg_type = agg_type\n        self.metrics = metrics.Metrics()\n        self.b_ts = b_test_set.BanditTestSet(config, profile)\n        self.scores = []\n\n    def get_skipped(self):\n        ret = []\n        # \"skip\" is a tuple of name and reason, decode just the name\n        for skip in self.skipped:\n            if isinstance(skip[0], bytes):\n                ret.append((skip[0].decode(\"utf-8\"), skip[1]))\n            else:\n                ret.append(skip)\n        return ret\n\n    def get_issue_list(\n        self, sev_level=b_constants.LOW, conf_level=b_constants.LOW\n    ):\n        return self.filter_results(sev_level, conf_level)\n\n    def populate_baseline(self, data):\n        \"\"\"Populate a baseline set of issues from a JSON report\n\n        This will populate a list of baseline issues discovered from a previous\n        run of bandit. Later this baseline can be used to filter out the result\n        set, see filter_results.\n        \"\"\"\n        items = []\n        try:\n            jdata = json.loads(data)\n            items = [issue.issue_from_dict(j) for j in jdata[\"results\"]]\n        except Exception as e:\n            LOG.warning(\"Failed to load baseline data: %s\", e)\n        self.baseline = items\n\n    def filter_results(self, sev_filter, conf_filter):\n        \"\"\"Returns a list of results filtered by the baseline\n\n        This works by checking the number of results returned from each file we\n        process. If the number of results is different to the number reported\n        for the same file in the baseline, then we return all results for the\n        file. We can't reliably return just the new results, as line numbers\n        will likely have changed.\n\n        :param sev_filter: severity level filter to apply\n        :param conf_filter: confidence level filter to apply\n        \"\"\"\n\n        results = [\n            i for i in self.results if i.filter(sev_filter, conf_filter)\n        ]\n\n        if not self.baseline:\n            return results\n\n        unmatched = _compare_baseline_results(self.baseline, results)\n        # if it's a baseline we'll return a dictionary of issues and a list of\n        # candidate issues\n        return _find_candidate_matches(unmatched, results)\n\n    def results_count(\n        self, sev_filter=b_constants.LOW, conf_filter=b_constants.LOW\n    ):\n        \"\"\"Return the count of results\n\n        :param sev_filter: Severity level to filter lower\n        :param conf_filter: Confidence level to filter\n        :return: Number of results in the set\n        \"\"\"\n        return len(self.get_issue_list(sev_filter, conf_filter))\n\n    def output_results(\n        self,\n        lines,\n        sev_level,\n        conf_level,\n        output_file,\n        output_format,\n        template=None,\n    ):\n        \"\"\"Outputs results from the result store\n\n        :param lines: How many surrounding lines to show per result\n        :param sev_level: Which severity levels to show (LOW, MEDIUM, HIGH)\n        :param conf_level: Which confidence levels to show (LOW, MEDIUM, HIGH)\n        :param output_file: File to store results\n        :param output_format: output format plugin name\n        :param template: Output template with non-terminal tags <N>\n                         (default:  {abspath}:{line}:\n                         {test_id}[bandit]: {severity}: {msg})\n        :return: -\n        \"\"\"\n        try:\n            formatters_mgr = extension_loader.MANAGER.formatters_mgr\n            if output_format not in formatters_mgr:\n                output_format = (\n                    \"screen\"\n                    if (\n                        sys.stdout.isatty()\n                        and os.getenv(\"NO_COLOR\") is None\n                        and os.getenv(\"TERM\") != \"dumb\"\n                    )\n                    else \"txt\"\n                )\n\n            formatter = formatters_mgr[output_format]\n            report_func = formatter.plugin\n            if output_format == \"custom\":\n                report_func(\n                    self,\n                    fileobj=output_file,\n                    sev_level=sev_level,\n                    conf_level=conf_level,\n                    template=template,\n                )\n            else:\n                report_func(\n                    self,\n                    fileobj=output_file,\n                    sev_level=sev_level,\n                    conf_level=conf_level,\n                    lines=lines,\n                )\n\n        except Exception as e:\n            raise RuntimeError(\n                f\"Unable to output report using \"\n                f\"'{output_format}' formatter: {str(e)}\"\n            )\n\n    def discover_files(self, targets, recursive=False, excluded_paths=\"\"):\n        \"\"\"Add tests directly and from a directory to the test set\n\n        :param targets: The command line list of files and directories\n        :param recursive: True/False - whether to add all files from dirs\n        :return:\n        \"\"\"\n        # We'll maintain a list of files which are added, and ones which have\n        # been explicitly excluded\n        files_list = set()\n        excluded_files = set()\n\n        excluded_path_globs = self.b_conf.get_option(\"exclude_dirs\") or []\n        included_globs = self.b_conf.get_option(\"include\") or [\"*.py\"]\n\n        # if there are command line provided exclusions add them to the list\n        if excluded_paths:\n            for path in excluded_paths.split(\",\"):\n                if os.path.isdir(path):\n                    path = os.path.join(path, \"*\")\n\n                excluded_path_globs.append(path)\n\n        # build list of files we will analyze\n        for fname in targets:\n            # if this is a directory and recursive is set, find all files\n            if os.path.isdir(fname):\n                if recursive:\n                    new_files, newly_excluded = _get_files_from_dir(\n                        fname,\n                        included_globs=included_globs,\n                        excluded_path_strings=excluded_path_globs,\n                    )\n                    files_list.update(new_files)\n                    excluded_files.update(newly_excluded)\n                else:\n                    LOG.warning(\n                        \"Skipping directory (%s), use -r flag to \"\n                        \"scan contents\",\n                        fname,\n                    )\n\n            else:\n                # if the user explicitly mentions a file on command line,\n                # we'll scan it, regardless of whether it's in the included\n                # file types list\n                if _is_file_included(\n                    fname,\n                    included_globs,\n                    excluded_path_globs,\n                    enforce_glob=False,\n                ):\n                    if fname != \"-\":\n                        fname = os.path.join(\".\", fname)\n                    files_list.add(fname)\n                else:\n                    excluded_files.add(fname)\n\n        self.files_list = sorted(files_list)\n        self.excluded_files = sorted(excluded_files)\n\n    def run_tests(self):\n        \"\"\"Runs through all files in the scope\n\n        :return: -\n        \"\"\"\n        # if we have problems with a file, we'll remove it from the files_list\n        # and add it to the skipped list instead\n        new_files_list = list(self.files_list)\n        if (\n            len(self.files_list) > PROGRESS_THRESHOLD\n            and LOG.getEffectiveLevel() <= logging.INFO\n        ):\n            files = progress.track(self.files_list)\n        else:\n            files = self.files_list\n\n        for count, fname in enumerate(files):\n            LOG.debug(\"working on file : %s\", fname)\n\n            try:\n                if fname == \"-\":\n                    open_fd = os.fdopen(sys.stdin.fileno(), \"rb\", 0)\n                    fdata = io.BytesIO(open_fd.read())\n                    new_files_list = [\n                        \"<stdin>\" if x == \"-\" else x for x in new_files_list\n                    ]\n                    self._parse_file(\"<stdin>\", fdata, new_files_list)\n                else:\n                    with open(fname, \"rb\") as fdata:\n                        self._parse_file(fname, fdata, new_files_list)\n            except OSError as e:\n                self.skipped.append((fname, e.strerror))\n                new_files_list.remove(fname)\n\n        # reflect any files which may have been skipped\n        self.files_list = new_files_list\n\n        # do final aggregation of metrics\n        self.metrics.aggregate()\n\n    def _parse_file(self, fname, fdata, new_files_list):\n        try:\n            # parse the current file\n            data = fdata.read()\n            lines = data.splitlines()\n            self.metrics.begin(fname)\n            self.metrics.count_locs(lines)\n            # nosec_lines is a dict of line number -> set of tests to ignore\n            #                                         for the line\n            nosec_lines = dict()\n            try:\n                fdata.seek(0)\n                tokens = tokenize.tokenize(fdata.readline)\n\n                if not self.ignore_nosec:\n                    for toktype, tokval, (lineno, _), _, _ in tokens:\n                        if toktype == tokenize.COMMENT:\n                            nosec_lines[lineno] = _parse_nosec_comment(tokval)\n\n            except tokenize.TokenError:\n                pass\n            score = self._execute_ast_visitor(fname, fdata, data, nosec_lines)\n            self.scores.append(score)\n            self.metrics.count_issues([score])\n        except KeyboardInterrupt:\n            sys.exit(2)\n        except SyntaxError:\n            self.skipped.append(\n                (fname, \"syntax error while parsing AST from file\")\n            )\n            new_files_list.remove(fname)\n        except Exception as e:\n            LOG.error(\n                \"Exception occurred when executing tests against %s.\", fname\n            )\n            if not LOG.isEnabledFor(logging.DEBUG):\n                LOG.error(\n                    'Run \"bandit --debug %s\" to see the full traceback.', fname\n                )\n\n            self.skipped.append((fname, \"exception while scanning file\"))\n            new_files_list.remove(fname)\n            LOG.debug(\"  Exception string: %s\", e)\n            LOG.debug(\"  Exception traceback: %s\", traceback.format_exc())\n\n    def _execute_ast_visitor(self, fname, fdata, data, nosec_lines):\n        \"\"\"Execute AST parse on each file\n\n        :param fname: The name of the file being parsed\n        :param data: Original file contents\n        :param lines: The lines of code to process\n        :return: The accumulated test score\n        \"\"\"\n        score = []\n        res = b_node_visitor.BanditNodeVisitor(\n            fname,\n            fdata,\n            self.b_ma,\n            self.b_ts,\n            self.debug,\n            nosec_lines,\n            self.metrics,\n        )\n\n        score = res.process(data)\n        self.results.extend(res.tester.results)\n        return score\n\n\ndef _get_files_from_dir(\n    files_dir, included_globs=None, excluded_path_strings=None\n):\n    if not included_globs:\n        included_globs = [\"*.py\"]\n    if not excluded_path_strings:\n        excluded_path_strings = []\n\n    files_list = set()\n    excluded_files = set()\n\n    for root, _, files in os.walk(files_dir):\n        for filename in files:\n            path = os.path.join(root, filename)\n            if _is_file_included(path, included_globs, excluded_path_strings):\n                files_list.add(path)\n            else:\n                excluded_files.add(path)\n\n    return files_list, excluded_files\n\n\ndef _is_file_included(\n    path, included_globs, excluded_path_strings, enforce_glob=True\n):\n    \"\"\"Determine if a file should be included based on filename\n\n    This utility function determines if a file should be included based\n    on the file name, a list of parsed extensions, excluded paths, and a flag\n    specifying whether extensions should be enforced.\n\n    :param path: Full path of file to check\n    :param parsed_extensions: List of parsed extensions\n    :param excluded_paths: List of paths (globbing supported) from which we\n        should not include files\n    :param enforce_glob: Can set to false to bypass extension check\n    :return: Boolean indicating whether a file should be included\n    \"\"\"\n    return_value = False\n\n    # if this is matches a glob of files we look at, and it isn't in an\n    # excluded path\n    if _matches_glob_list(path, included_globs) or not enforce_glob:\n        if not _matches_glob_list(path, excluded_path_strings) and not any(\n            x in path for x in excluded_path_strings\n        ):\n            return_value = True\n\n    return return_value\n\n\ndef _matches_glob_list(filename, glob_list):\n    for glob in glob_list:\n        if fnmatch.fnmatch(filename, glob):\n            return True\n    return False\n\n\ndef _compare_baseline_results(baseline, results):\n    \"\"\"Compare a baseline list of issues to list of results\n\n    This function compares a baseline set of issues to a current set of issues\n    to find results that weren't present in the baseline.\n\n    :param baseline: Baseline list of issues\n    :param results: Current list of issues\n    :return: List of unmatched issues\n    \"\"\"\n    return [a for a in results if a not in baseline]\n\n\ndef _find_candidate_matches(unmatched_issues, results_list):\n    \"\"\"Returns a dictionary with issue candidates\n\n    For example, let's say we find a new command injection issue in a file\n    which used to have two.  Bandit can't tell which of the command injection\n    issues in the file are new, so it will show all three.  The user should\n    be able to pick out the new one.\n\n    :param unmatched_issues: List of issues that weren't present before\n    :param results_list: main list of current Bandit findings\n    :return: A dictionary with a list of candidates for each issue\n    \"\"\"\n\n    issue_candidates = collections.OrderedDict()\n\n    for unmatched in unmatched_issues:\n        issue_candidates[unmatched] = [\n            i for i in results_list if unmatched == i\n        ]\n\n    return issue_candidates\n\n\ndef _find_test_id_from_nosec_string(extman, match):\n    test_id = extman.check_id(match)\n    if test_id:\n        return match\n    # Finding by short_id didn't work, let's check the test name\n    test_id = extman.get_test_id(match)\n    if not test_id:\n        # Name and short id didn't work:\n        LOG.warning(\n            \"Test in comment: %s is not a test name or id, ignoring\", match\n        )\n    return test_id  # We want to return None or the string here regardless\n\n\ndef _parse_nosec_comment(comment):\n    found_no_sec_comment = NOSEC_COMMENT.search(comment)\n    if not found_no_sec_comment:\n        # there was no nosec comment\n        return None\n\n    matches = found_no_sec_comment.groupdict()\n    nosec_tests = matches.get(\"tests\", set())\n\n    # empty set indicates that there was a nosec comment without specific\n    # test ids or names\n    test_ids = set()\n    if nosec_tests:\n        extman = extension_loader.MANAGER\n        # lookup tests by short code or name\n        for test in NOSEC_COMMENT_TESTS.finditer(nosec_tests):\n            test_match = test.group(1)\n            test_id = _find_test_id_from_nosec_string(extman, test_match)\n            if test_id:\n                test_ids.add(test_id)\n\n    return test_ids\n"
  },
  {
    "path": "bandit/core/meta_ast.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport logging\n\nLOG = logging.getLogger(__name__)\n\n\nclass BanditMetaAst:\n    nodes = collections.OrderedDict()\n\n    def __init__(self):\n        pass\n\n    def add_node(self, node, parent_id, depth):\n        \"\"\"Add a node to the AST node collection\n\n        :param node: The AST node to add\n        :param parent_id: The ID of the node's parent\n        :param depth: The depth of the node\n        :return: -\n        \"\"\"\n        node_id = hex(id(node))\n        LOG.debug(\"adding node : %s [%s]\", node_id, depth)\n        self.nodes[node_id] = {\n            \"raw\": node,\n            \"parent_id\": parent_id,\n            \"depth\": depth,\n        }\n\n    def __str__(self):\n        \"\"\"Dumps a listing of all of the nodes\n\n        Dumps a listing of all of the nodes for debugging purposes\n        :return: -\n        \"\"\"\n        tmpstr = \"\"\n        for k, v in self.nodes.items():\n            tmpstr += f\"Node: {k}\\n\"\n            tmpstr += f\"\\t{str(v)}\\n\"\n        tmpstr += f\"Length: {len(self.nodes)}\\n\"\n        return tmpstr\n"
  },
  {
    "path": "bandit/core/metrics.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\n\nfrom bandit.core import constants\n\n\nclass Metrics:\n    \"\"\"Bandit metric gathering.\n\n    This class is a singleton used to gather and process metrics collected when\n    processing a code base with bandit. Metric collection is stateful, that\n    is, an active metric block will be set when requested and all subsequent\n    operations will effect that metric block until it is replaced by a setting\n    a new one.\n    \"\"\"\n\n    def __init__(self):\n        self.data = dict()\n        self.data[\"_totals\"] = {\n            \"loc\": 0,\n            \"nosec\": 0,\n            \"skipped_tests\": 0,\n        }\n\n        # initialize 0 totals for criteria and rank; this will be reset later\n        for rank in constants.RANKING:\n            for criteria in constants.CRITERIA:\n                self.data[\"_totals\"][f\"{criteria[0]}.{rank}\"] = 0\n\n    def begin(self, fname):\n        \"\"\"Begin a new metric block.\n\n        This starts a new metric collection name \"fname\" and makes is active.\n        :param fname: the metrics unique name, normally the file name.\n        \"\"\"\n        self.data[fname] = {\n            \"loc\": 0,\n            \"nosec\": 0,\n            \"skipped_tests\": 0,\n        }\n        self.current = self.data[fname]\n\n    def note_nosec(self, num=1):\n        \"\"\"Note a \"nosec\" comment.\n\n        Increment the currently active metrics nosec count.\n        :param num: number of nosecs seen, defaults to 1\n        \"\"\"\n        self.current[\"nosec\"] += num\n\n    def note_skipped_test(self, num=1):\n        \"\"\"Note a \"nosec BXXX, BYYY, ...\" comment.\n\n        Increment the currently active metrics skipped_tests count.\n        :param num: number of skipped_tests seen, defaults to 1\n        \"\"\"\n        self.current[\"skipped_tests\"] += num\n\n    def count_locs(self, lines):\n        \"\"\"Count lines of code.\n\n        We count lines that are not empty and are not comments. The result is\n        added to our currently active metrics loc count (normally this is 0).\n\n        :param lines: lines in the file to process\n        \"\"\"\n\n        def proc(line):\n            tmp = line.strip()\n            return bool(tmp and not tmp.startswith(b\"#\"))\n\n        self.current[\"loc\"] += sum(proc(line) for line in lines)\n\n    def count_issues(self, scores):\n        self.current.update(self._get_issue_counts(scores))\n\n    def aggregate(self):\n        \"\"\"Do final aggregation of metrics.\"\"\"\n        c = collections.Counter()\n        for fname in self.data:\n            c.update(self.data[fname])\n        self.data[\"_totals\"] = dict(c)\n\n    @staticmethod\n    def _get_issue_counts(scores):\n        \"\"\"Get issue counts aggregated by confidence/severity rankings.\n\n        :param scores: list of scores to aggregate / count\n        :return: aggregated total (count) of issues identified\n        \"\"\"\n        issue_counts = {}\n        for score in scores:\n            for criteria, _ in constants.CRITERIA:\n                for i, rank in enumerate(constants.RANKING):\n                    label = f\"{criteria}.{rank}\"\n                    if label not in issue_counts:\n                        issue_counts[label] = 0\n                        count = (\n                            score[criteria][i]\n                            // constants.RANKING_VALUES[rank]\n                        )\n                        issue_counts[label] += count\n        return issue_counts\n"
  },
  {
    "path": "bandit/core/node_visitor.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\nimport logging\nimport operator\n\nfrom bandit.core import constants\nfrom bandit.core import tester as b_tester\nfrom bandit.core import utils as b_utils\n\nLOG = logging.getLogger(__name__)\n\n\nclass BanditNodeVisitor:\n    def __init__(\n        self, fname, fdata, metaast, testset, debug, nosec_lines, metrics\n    ):\n        self.debug = debug\n        self.nosec_lines = nosec_lines\n        self.scores = {\n            \"SEVERITY\": [0] * len(constants.RANKING),\n            \"CONFIDENCE\": [0] * len(constants.RANKING),\n        }\n        self.depth = 0\n        self.fname = fname\n        self.fdata = fdata\n        self.metaast = metaast\n        self.testset = testset\n        self.imports = set()\n        self.import_aliases = {}\n        self.tester = b_tester.BanditTester(\n            self.testset, self.debug, nosec_lines, metrics\n        )\n\n        # in some cases we can't determine a qualified name\n        try:\n            self.namespace = b_utils.get_module_qualname_from_path(fname)\n        except b_utils.InvalidModulePath:\n            LOG.warning(\n                \"Unable to find qualified name for module: %s\", self.fname\n            )\n            self.namespace = \"\"\n        LOG.debug(\"Module qualified name: %s\", self.namespace)\n        self.metrics = metrics\n\n    def visit_ClassDef(self, node):\n        \"\"\"Visitor for AST ClassDef node\n\n        Add class name to current namespace for all descendants.\n        :param node: Node being inspected\n        :return: -\n        \"\"\"\n        # For all child nodes, add this class name to current namespace\n        self.namespace = b_utils.namespace_path_join(self.namespace, node.name)\n\n    def visit_FunctionDef(self, node):\n        \"\"\"Visitor for AST FunctionDef nodes\n\n        add relevant information about the node to\n        the context for use in tests which inspect function definitions.\n        Add the function name to the current namespace for all descendants.\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n\n        self.context[\"function\"] = node\n        qualname = self.namespace + \".\" + b_utils.get_func_name(node)\n        name = qualname.split(\".\")[-1]\n\n        self.context[\"qualname\"] = qualname\n        self.context[\"name\"] = name\n\n        # For all child nodes and any tests run, add this function name to\n        # current namespace\n        self.namespace = b_utils.namespace_path_join(self.namespace, name)\n        self.update_scores(self.tester.run_tests(self.context, \"FunctionDef\"))\n\n    def visit_Call(self, node):\n        \"\"\"Visitor for AST Call nodes\n\n        add relevant information about the node to\n        the context for use in tests which inspect function calls.\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n\n        self.context[\"call\"] = node\n        qualname = b_utils.get_call_name(node, self.import_aliases)\n        name = qualname.split(\".\")[-1]\n\n        self.context[\"qualname\"] = qualname\n        self.context[\"name\"] = name\n\n        self.update_scores(self.tester.run_tests(self.context, \"Call\"))\n\n    def visit_Import(self, node):\n        \"\"\"Visitor for AST Import nodes\n\n        add relevant information about node to\n        the context for use in tests which inspect imports.\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n        for nodename in node.names:\n            if nodename.asname:\n                self.import_aliases[nodename.asname] = nodename.name\n            self.imports.add(nodename.name)\n            self.context[\"module\"] = nodename.name\n        self.update_scores(self.tester.run_tests(self.context, \"Import\"))\n\n    def visit_ImportFrom(self, node):\n        \"\"\"Visitor for AST ImportFrom nodes\n\n        add relevant information about node to\n        the context for use in tests which inspect imports.\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n        module = node.module\n        if module is None:\n            return self.visit_Import(node)\n\n        for nodename in node.names:\n            # TODO(ljfisher) Names in import_aliases could be overridden\n            #      by local definitions. If this occurs bandit will see the\n            #      name in import_aliases instead of the local definition.\n            #      We need better tracking of names.\n            if nodename.asname:\n                self.import_aliases[nodename.asname] = (\n                    module + \".\" + nodename.name\n                )\n            else:\n                # Even if import is not aliased we need an entry that maps\n                # name to module.name.  For example, with 'from a import b'\n                # b should be aliased to the qualified name a.b\n                self.import_aliases[nodename.name] = (\n                    module + \".\" + nodename.name\n                )\n            self.imports.add(module + \".\" + nodename.name)\n            self.context[\"module\"] = module\n            self.context[\"name\"] = nodename.name\n        self.update_scores(self.tester.run_tests(self.context, \"ImportFrom\"))\n\n    def visit_Constant(self, node):\n        \"\"\"Visitor for AST Constant nodes\n\n        call the appropriate method for the node type.\n        this maintains compatibility with <3.6 and 3.8+\n\n        This code is heavily influenced by Anthony Sottile (@asottile) here:\n        https://bugs.python.org/msg342486\n\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n        if isinstance(node.value, str):\n            self.visit_Str(node)\n        elif isinstance(node.value, bytes):\n            self.visit_Bytes(node)\n\n    def visit_Str(self, node):\n        \"\"\"Visitor for AST String nodes\n\n        add relevant information about node to\n        the context for use in tests which inspect strings.\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n        self.context[\"str\"] = node.value\n        if not isinstance(node._bandit_parent, ast.Expr):  # docstring\n            self.context[\"linerange\"] = b_utils.linerange(node._bandit_parent)\n            self.update_scores(self.tester.run_tests(self.context, \"Str\"))\n\n    def visit_Bytes(self, node):\n        \"\"\"Visitor for AST Bytes nodes\n\n        add relevant information about node to\n        the context for use in tests which inspect strings.\n        :param node: The node that is being inspected\n        :return: -\n        \"\"\"\n        self.context[\"bytes\"] = node.value\n        if not isinstance(node._bandit_parent, ast.Expr):  # docstring\n            self.context[\"linerange\"] = b_utils.linerange(node._bandit_parent)\n            self.update_scores(self.tester.run_tests(self.context, \"Bytes\"))\n\n    def pre_visit(self, node):\n        self.context = {}\n        self.context[\"imports\"] = self.imports\n        self.context[\"import_aliases\"] = self.import_aliases\n\n        if self.debug:\n            LOG.debug(ast.dump(node))\n            self.metaast.add_node(node, \"\", self.depth)\n\n        if hasattr(node, \"lineno\"):\n            self.context[\"lineno\"] = node.lineno\n\n        if hasattr(node, \"col_offset\"):\n            self.context[\"col_offset\"] = node.col_offset\n        if hasattr(node, \"end_col_offset\"):\n            self.context[\"end_col_offset\"] = node.end_col_offset\n\n        self.context[\"node\"] = node\n        self.context[\"linerange\"] = b_utils.linerange(node)\n        self.context[\"filename\"] = self.fname\n        self.context[\"file_data\"] = self.fdata\n\n        LOG.debug(\n            \"entering: %s %s [%s]\", hex(id(node)), type(node), self.depth\n        )\n        self.depth += 1\n        LOG.debug(self.context)\n        return True\n\n    def visit(self, node):\n        name = node.__class__.__name__\n        method = \"visit_\" + name\n        visitor = getattr(self, method, None)\n        if visitor is not None:\n            if self.debug:\n                LOG.debug(\"%s called (%s)\", method, ast.dump(node))\n            visitor(node)\n        else:\n            self.update_scores(self.tester.run_tests(self.context, name))\n\n    def post_visit(self, node):\n        self.depth -= 1\n        LOG.debug(\"%s\\texiting : %s\", self.depth, hex(id(node)))\n\n        # HACK(tkelsey): this is needed to clean up post-recursion stuff that\n        # gets setup in the visit methods for these node types.\n        if isinstance(node, (ast.FunctionDef, ast.ClassDef)):\n            self.namespace = b_utils.namespace_path_split(self.namespace)[0]\n\n    def generic_visit(self, node):\n        \"\"\"Drive the visitor.\"\"\"\n        for _, value in ast.iter_fields(node):\n            if isinstance(value, list):\n                max_idx = len(value) - 1\n                for idx, item in enumerate(value):\n                    if isinstance(item, ast.AST):\n                        if idx < max_idx:\n                            item._bandit_sibling = value[idx + 1]\n                        else:\n                            item._bandit_sibling = None\n                        item._bandit_parent = node\n\n                        if self.pre_visit(item):\n                            self.visit(item)\n                            self.generic_visit(item)\n                            self.post_visit(item)\n\n            elif isinstance(value, ast.AST):\n                value._bandit_sibling = None\n                value._bandit_parent = node\n                if self.pre_visit(value):\n                    self.visit(value)\n                    self.generic_visit(value)\n                    self.post_visit(value)\n\n    def update_scores(self, scores):\n        \"\"\"Score updater\n\n        Since we moved from a single score value to a map of scores per\n        severity, this is needed to update the stored list.\n        :param score: The score list to update our scores with\n        \"\"\"\n        # we'll end up with something like:\n        # SEVERITY: {0, 0, 0, 10}  where 10 is weighted by finding and level\n        for score_type in self.scores:\n            self.scores[score_type] = list(\n                map(operator.add, self.scores[score_type], scores[score_type])\n            )\n\n    def process(self, data):\n        \"\"\"Main process loop\n\n        Build and process the AST\n        :param lines: lines code to process\n        :return score: the aggregated score for the current file\n        \"\"\"\n        f_ast = ast.parse(data)\n        self.generic_visit(f_ast)\n        # Run tests that do not require access to the AST,\n        # but only to the whole file source:\n        self.context = {\n            \"file_data\": self.fdata,\n            \"filename\": self.fname,\n            \"lineno\": 0,\n            \"linerange\": [0, 1],\n            \"col_offset\": 0,\n        }\n        self.update_scores(self.tester.run_tests(self.context, \"File\"))\n        return self.scores\n"
  },
  {
    "path": "bandit/core/test_properties.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport logging\n\nfrom bandit.core import utils\n\nLOG = logging.getLogger(__name__)\n\n\ndef checks(*args):\n    \"\"\"Decorator function to set checks to be run.\"\"\"\n\n    def wrapper(func):\n        if not hasattr(func, \"_checks\"):\n            func._checks = []\n        for arg in args:\n            if arg == \"File\":\n                func._checks.append(\"File\")\n            else:\n                func._checks.append(utils.check_ast_node(arg))\n\n        LOG.debug(\"checks() decorator executed\")\n        LOG.debug(\"  func._checks: %s\", func._checks)\n        return func\n\n    return wrapper\n\n\ndef takes_config(*args):\n    \"\"\"Test function takes config\n\n    Use of this delegate before a test function indicates that it should be\n    passed data from the config file. Passing a name parameter allows\n    aliasing tests and thus sharing config options.\n    \"\"\"\n    name = \"\"\n\n    def _takes_config(func):\n        if not hasattr(func, \"_takes_config\"):\n            func._takes_config = name\n        return func\n\n    if len(args) == 1 and callable(args[0]):\n        name = args[0].__name__\n        return _takes_config(args[0])\n    else:\n        name = args[0]\n        return _takes_config\n\n\ndef test_id(id_val):\n    \"\"\"Test function identifier\n\n    Use this decorator before a test function indicates its simple ID\n    \"\"\"\n\n    def _has_id(func):\n        if not hasattr(func, \"_test_id\"):\n            func._test_id = id_val\n        return func\n\n    return _has_id\n\n\ndef accepts_baseline(*args):\n    \"\"\"Decorator to indicate formatter accepts baseline results\n\n    Use of this decorator before a formatter indicates that it is able to deal\n    with baseline results.  Specifically this means it has a way to display\n    candidate results and know when it should do so.\n    \"\"\"\n\n    def wrapper(func):\n        if not hasattr(func, \"_accepts_baseline\"):\n            func._accepts_baseline = True\n\n        LOG.debug(\"accepts_baseline() decorator executed on %s\", func.__name__)\n\n        return func\n\n    return wrapper(args[0])\n"
  },
  {
    "path": "bandit/core/test_set.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport importlib\nimport logging\n\nfrom bandit.core import blacklisting\nfrom bandit.core import extension_loader\n\nLOG = logging.getLogger(__name__)\n\n\nclass BanditTestSet:\n    def __init__(self, config, profile=None):\n        if not profile:\n            profile = {}\n        extman = extension_loader.MANAGER\n        filtering = self._get_filter(config, profile)\n        self.plugins = [\n            p for p in extman.plugins if p.plugin._test_id in filtering\n        ]\n        self.plugins.extend(self._load_builtins(filtering, profile))\n        self._load_tests(config, self.plugins)\n\n    @staticmethod\n    def _get_filter(config, profile):\n        extman = extension_loader.MANAGER\n\n        inc = set(profile.get(\"include\", []))\n        exc = set(profile.get(\"exclude\", []))\n\n        all_blacklist_tests = set()\n        for _, tests in extman.blacklist.items():\n            all_blacklist_tests.update(t[\"id\"] for t in tests)\n\n        # this block is purely for backwards compatibility, the rules are as\n        # follows:\n        # B001,B401 means B401\n        # B401 means B401\n        # B001 means all blacklist tests\n        if \"B001\" in inc:\n            if not inc.intersection(all_blacklist_tests):\n                inc.update(all_blacklist_tests)\n            inc.discard(\"B001\")\n        if \"B001\" in exc:\n            if not exc.intersection(all_blacklist_tests):\n                exc.update(all_blacklist_tests)\n            exc.discard(\"B001\")\n\n        if inc:\n            filtered = inc\n        else:\n            filtered = set(extman.plugins_by_id.keys())\n            filtered.update(extman.builtin)\n            filtered.update(all_blacklist_tests)\n        return filtered - exc\n\n    def _load_builtins(self, filtering, profile):\n        \"\"\"loads up builtin functions, so they can be filtered.\"\"\"\n\n        class Wrapper:\n            def __init__(self, name, plugin):\n                self.name = name\n                self.plugin = plugin\n\n        extman = extension_loader.MANAGER\n        blacklist = profile.get(\"blacklist\")\n        if not blacklist:  # not overridden by legacy data\n            blacklist = {}\n            for node, tests in extman.blacklist.items():\n                values = [t for t in tests if t[\"id\"] in filtering]\n                if values:\n                    blacklist[node] = values\n\n        if not blacklist:\n            return []\n\n        # this dresses up the blacklist to look like a plugin, but\n        # the '_checks' data comes from the blacklist information.\n        # the '_config' is the filtered blacklist data set.\n        blacklisting.blacklist._test_id = \"B001\"\n        blacklisting.blacklist._checks = blacklist.keys()\n        blacklisting.blacklist._config = blacklist\n\n        return [Wrapper(\"blacklist\", blacklisting.blacklist)]\n\n    def _load_tests(self, config, plugins):\n        \"\"\"Builds a dict mapping tests to node types.\"\"\"\n        self.tests = {}\n        for plugin in plugins:\n            if hasattr(plugin.plugin, \"_takes_config\"):\n                # TODO(??): config could come from profile ...\n                cfg = config.get_option(plugin.plugin._takes_config)\n                if cfg is None:\n                    genner = importlib.import_module(plugin.plugin.__module__)\n                    cfg = genner.gen_config(plugin.plugin._takes_config)\n                plugin.plugin._config = cfg\n            for check in plugin.plugin._checks:\n                self.tests.setdefault(check, []).append(plugin.plugin)\n                LOG.debug(\n                    \"added function %s (%s) targeting %s\",\n                    plugin.name,\n                    plugin.plugin._test_id,\n                    check,\n                )\n\n    def get_tests(self, checktype):\n        \"\"\"Returns all tests that are of type checktype\n\n        :param checktype: The type of test to filter on\n        :return: A list of tests which are of the specified type\n        \"\"\"\n        return self.tests.get(checktype) or []\n"
  },
  {
    "path": "bandit/core/tester.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport copy\nimport logging\nimport warnings\n\nfrom bandit.core import constants\nfrom bandit.core import context as b_context\nfrom bandit.core import utils\n\nwarnings.formatwarning = utils.warnings_formatter\nLOG = logging.getLogger(__name__)\n\n\nclass BanditTester:\n    def __init__(self, testset, debug, nosec_lines, metrics):\n        self.results = []\n        self.testset = testset\n        self.last_result = None\n        self.debug = debug\n        self.nosec_lines = nosec_lines\n        self.metrics = metrics\n\n    def run_tests(self, raw_context, checktype):\n        \"\"\"Runs all tests for a certain type of check, for example\n\n        Runs all tests for a certain type of check, for example 'functions'\n        store results in results.\n\n        :param raw_context: Raw context dictionary\n        :param checktype: The type of checks to run\n        :return: a score based on the number and type of test results with\n                extra metrics about nosec comments\n        \"\"\"\n\n        scores = {\n            \"SEVERITY\": [0] * len(constants.RANKING),\n            \"CONFIDENCE\": [0] * len(constants.RANKING),\n        }\n\n        tests = self.testset.get_tests(checktype)\n        for test in tests:\n            name = test.__name__\n            # execute test with an instance of the context class\n            temp_context = copy.copy(raw_context)\n            context = b_context.Context(temp_context)\n            try:\n                if hasattr(test, \"_config\"):\n                    result = test(context, test._config)\n                else:\n                    result = test(context)\n\n                if result is not None:\n                    nosec_tests_to_skip = self._get_nosecs_from_contexts(\n                        temp_context, test_result=result\n                    )\n\n                    if isinstance(temp_context[\"filename\"], bytes):\n                        result.fname = temp_context[\"filename\"].decode(\"utf-8\")\n                    else:\n                        result.fname = temp_context[\"filename\"]\n                    result.fdata = temp_context[\"file_data\"]\n\n                    if result.lineno is None:\n                        result.lineno = temp_context[\"lineno\"]\n                    if result.linerange == []:\n                        result.linerange = temp_context[\"linerange\"]\n                    if result.col_offset == -1:\n                        result.col_offset = temp_context[\"col_offset\"]\n                    result.end_col_offset = temp_context.get(\n                        \"end_col_offset\", 0\n                    )\n                    result.test = name\n                    if result.test_id == \"\":\n                        result.test_id = test._test_id\n\n                    # don't skip the test if there was no nosec comment\n                    if nosec_tests_to_skip is not None:\n                        # If the set is empty then it means that nosec was\n                        # used without test number -> update nosecs counter.\n                        # If the test id is in the set of tests to skip,\n                        # log and increment the skip by test count.\n                        if not nosec_tests_to_skip:\n                            LOG.debug(\"skipped, nosec without test number\")\n                            self.metrics.note_nosec()\n                            continue\n                        if result.test_id in nosec_tests_to_skip:\n                            LOG.debug(\n                                f\"skipped, nosec for test {result.test_id}\"\n                            )\n                            self.metrics.note_skipped_test()\n                            continue\n\n                    self.results.append(result)\n\n                    LOG.debug(\"Issue identified by %s: %s\", name, result)\n                    sev = constants.RANKING.index(result.severity)\n                    val = constants.RANKING_VALUES[result.severity]\n                    scores[\"SEVERITY\"][sev] += val\n                    con = constants.RANKING.index(result.confidence)\n                    val = constants.RANKING_VALUES[result.confidence]\n                    scores[\"CONFIDENCE\"][con] += val\n                else:\n                    nosec_tests_to_skip = self._get_nosecs_from_contexts(\n                        temp_context\n                    )\n                    if (\n                        nosec_tests_to_skip\n                        and test._test_id in nosec_tests_to_skip\n                    ):\n                        LOG.warning(\n                            f\"nosec encountered ({test._test_id}), but no \"\n                            f\"failed test on file \"\n                            f\"{temp_context['filename']}:\"\n                            f\"{temp_context['lineno']}\"\n                        )\n\n            except Exception as e:\n                self.report_error(name, context, e)\n                if self.debug:\n                    raise\n        LOG.debug(\"Returning scores: %s\", scores)\n        return scores\n\n    def _get_nosecs_from_contexts(self, context, test_result=None):\n        \"\"\"Use context and optional test result to get set of tests to skip.\n        :param context: temp context\n        :param test_result: optional test result\n        :return: set of tests to skip for the line based on contexts\n        \"\"\"\n        nosec_tests_to_skip = set()\n        base_tests = (\n            self.nosec_lines.get(test_result.lineno, None)\n            if test_result\n            else None\n        )\n        context_tests = utils.get_nosec(self.nosec_lines, context)\n\n        # if both are none there were no comments\n        # this is explicitly different from being empty.\n        # empty set indicates blanket nosec comment without\n        # individual test names or ids\n        if base_tests is None and context_tests is None:\n            nosec_tests_to_skip = None\n\n        # combine tests from current line and context line\n        if base_tests is not None:\n            nosec_tests_to_skip.update(base_tests)\n        if context_tests is not None:\n            nosec_tests_to_skip.update(context_tests)\n\n        return nosec_tests_to_skip\n\n    @staticmethod\n    def report_error(test, context, error):\n        what = \"Bandit internal error running: \"\n        what += f\"{test} \"\n        what += \"on file %s at line %i: \" % (\n            context._context[\"filename\"],\n            context._context[\"lineno\"],\n        )\n        what += str(error)\n        import traceback\n\n        what += traceback.format_exc()\n        LOG.error(what)\n"
  },
  {
    "path": "bandit/core/utils.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\nimport logging\nimport os.path\nimport sys\n\ntry:\n    import configparser\nexcept ImportError:\n    import ConfigParser as configparser\n\nLOG = logging.getLogger(__name__)\n\n\n\"\"\"Various helper functions.\"\"\"\n\n\ndef _get_attr_qual_name(node, aliases):\n    \"\"\"Get a the full name for the attribute node.\n\n    This will resolve a pseudo-qualified name for the attribute\n    rooted at node as long as all the deeper nodes are Names or\n    Attributes. This will give you how the code referenced the name but\n    will not tell you what the name actually refers to. If we\n    encounter a node without a static name we punt with an\n    empty string. If this encounters something more complex, such as\n    foo.mylist[0](a,b) we just return empty string.\n\n    :param node: AST Name or Attribute node\n    :param aliases: Import aliases dictionary\n    :returns: Qualified name referred to by the attribute or name.\n    \"\"\"\n    if isinstance(node, ast.Name):\n        if node.id in aliases:\n            return aliases[node.id]\n        return node.id\n    elif isinstance(node, ast.Attribute):\n        name = f\"{_get_attr_qual_name(node.value, aliases)}.{node.attr}\"\n        if name in aliases:\n            return aliases[name]\n        return name\n    else:\n        return \"\"\n\n\ndef get_call_name(node, aliases):\n    if isinstance(node.func, ast.Name):\n        if deepgetattr(node, \"func.id\") in aliases:\n            return aliases[deepgetattr(node, \"func.id\")]\n        return deepgetattr(node, \"func.id\")\n    elif isinstance(node.func, ast.Attribute):\n        return _get_attr_qual_name(node.func, aliases)\n    else:\n        return \"\"\n\n\ndef get_func_name(node):\n    return node.name  # TODO(tkelsey): get that qualname using enclosing scope\n\n\ndef get_qual_attr(node, aliases):\n    if isinstance(node, ast.Attribute):\n        try:\n            val = deepgetattr(node, \"value.id\")\n            if val in aliases:\n                prefix = aliases[val]\n            else:\n                prefix = deepgetattr(node, \"value.id\")\n        except Exception:\n            # NOTE(tkelsey): degrade gracefully when we can't get the fully\n            # qualified name for an attr, just return its base name.\n            prefix = \"\"\n\n        return f\"{prefix}.{node.attr}\"\n    else:\n        return \"\"  # TODO(tkelsey): process other node types\n\n\ndef deepgetattr(obj, attr):\n    \"\"\"Recurses through an attribute chain to get the ultimate value.\"\"\"\n    for key in attr.split(\".\"):\n        obj = getattr(obj, key)\n    return obj\n\n\nclass InvalidModulePath(Exception):\n    pass\n\n\nclass ConfigError(Exception):\n    \"\"\"Raised when the config file fails validation.\"\"\"\n\n    def __init__(self, message, config_file):\n        self.config_file = config_file\n        self.message = f\"{config_file} : {message}\"\n        super().__init__(self.message)\n\n\nclass ProfileNotFound(Exception):\n    \"\"\"Raised when chosen profile cannot be found.\"\"\"\n\n    def __init__(self, config_file, profile):\n        self.config_file = config_file\n        self.profile = profile\n        message = \"Unable to find profile ({}) in config file: {}\".format(\n            self.profile,\n            self.config_file,\n        )\n        super().__init__(message)\n\n\ndef warnings_formatter(\n    message, category=UserWarning, filename=\"\", lineno=-1, line=\"\"\n):\n    \"\"\"Monkey patch for warnings.warn to suppress cruft output.\"\"\"\n    return f\"{message}\\n\"\n\n\ndef get_module_qualname_from_path(path):\n    \"\"\"Get the module's qualified name by analysis of the path.\n\n    Resolve the absolute pathname and eliminate symlinks. This could result in\n    an incorrect name if symlinks are used to restructure the python lib\n    directory.\n\n    Starting from the right-most directory component look for __init__.py in\n    the directory component. If it exists then the directory name is part of\n    the module name. Move left to the subsequent directory components until a\n    directory is found without __init__.py.\n\n    :param: Path to module file. Relative paths will be resolved relative to\n            current working directory.\n    :return: fully qualified module name\n    \"\"\"\n\n    (head, tail) = os.path.split(path)\n    if head == \"\" or tail == \"\":\n        raise InvalidModulePath(\n            f'Invalid python file path: \"{path}\" Missing path or file name'\n        )\n\n    qname = [os.path.splitext(tail)[0]]\n    while head not in [\"/\", \".\", \"\"]:\n        if os.path.isfile(os.path.join(head, \"__init__.py\")):\n            (head, tail) = os.path.split(head)\n            qname.insert(0, tail)\n        else:\n            break\n\n    qualname = \".\".join(qname)\n    return qualname\n\n\ndef namespace_path_join(base, name):\n    \"\"\"Extend the current namespace path with an additional name\n\n    Take a namespace path (i.e., package.module.class) and extends it\n    with an additional name (i.e., package.module.class.subclass).\n    This is similar to how os.path.join works.\n\n    :param base: (String) The base namespace path.\n    :param name: (String) The new name to append to the base path.\n    :returns: (String) A new namespace path resulting from combination of\n              base and name.\n    \"\"\"\n    return f\"{base}.{name}\"\n\n\ndef namespace_path_split(path):\n    \"\"\"Split the namespace path into a pair (head, tail).\n\n    Tail will be the last namespace path component and head will\n    be everything leading up to that in the path. This is similar to\n    os.path.split.\n\n    :param path: (String) A namespace path.\n    :returns: (String, String) A tuple where the first component is the base\n              path and the second is the last path component.\n    \"\"\"\n    return tuple(path.rsplit(\".\", 1))\n\n\ndef escaped_bytes_representation(b):\n    \"\"\"PY3 bytes need escaping for comparison with other strings.\n\n    In practice it turns control characters into acceptable codepoints then\n    encodes them into bytes again to turn unprintable bytes into printable\n    escape sequences.\n\n    This is safe to do for the whole range 0..255 and result matches\n    unicode_escape on a unicode string.\n    \"\"\"\n    return b.decode(\"unicode_escape\").encode(\"unicode_escape\")\n\n\ndef calc_linerange(node):\n    \"\"\"Calculate linerange for subtree\"\"\"\n    if hasattr(node, \"_bandit_linerange\"):\n        return node._bandit_linerange\n\n    lines_min = 9999999999\n    lines_max = -1\n    if hasattr(node, \"lineno\"):\n        lines_min = node.lineno\n        lines_max = node.lineno\n    for n in ast.iter_child_nodes(node):\n        lines_minmax = calc_linerange(n)\n        lines_min = min(lines_min, lines_minmax[0])\n        lines_max = max(lines_max, lines_minmax[1])\n\n    node._bandit_linerange = (lines_min, lines_max)\n\n    return (lines_min, lines_max)\n\n\ndef linerange(node):\n    \"\"\"Get line number range from a node.\"\"\"\n    if hasattr(node, \"lineno\"):\n        return list(range(node.lineno, node.end_lineno + 1))\n    else:\n        if hasattr(node, \"_bandit_linerange_stripped\"):\n            lines_minmax = node._bandit_linerange_stripped\n            return list(range(lines_minmax[0], lines_minmax[1] + 1))\n\n        strip = {\n            \"body\": None,\n            \"orelse\": None,\n            \"handlers\": None,\n            \"finalbody\": None,\n        }\n        for key in strip.keys():\n            if hasattr(node, key):\n                strip[key] = getattr(node, key)\n                setattr(node, key, [])\n\n        lines_min = 9999999999\n        lines_max = -1\n        if hasattr(node, \"lineno\"):\n            lines_min = node.lineno\n            lines_max = node.lineno\n        for n in ast.iter_child_nodes(node):\n            lines_minmax = calc_linerange(n)\n            lines_min = min(lines_min, lines_minmax[0])\n            lines_max = max(lines_max, lines_minmax[1])\n\n        for key in strip.keys():\n            if strip[key] is not None:\n                setattr(node, key, strip[key])\n\n        if lines_max == -1:\n            lines_min = 0\n            lines_max = 1\n\n        node._bandit_linerange_stripped = (lines_min, lines_max)\n\n        lines = list(range(lines_min, lines_max + 1))\n\n        \"\"\"Try and work around a known Python bug with multi-line strings.\"\"\"\n        # deal with multiline strings lineno behavior (Python issue #16806)\n        if hasattr(node, \"_bandit_sibling\") and hasattr(\n            node._bandit_sibling, \"lineno\"\n        ):\n            start = min(lines)\n            delta = node._bandit_sibling.lineno - start\n            if delta > 1:\n                return list(range(start, node._bandit_sibling.lineno))\n        return lines\n\n\ndef concat_string(node, stop=None):\n    \"\"\"Builds a string from a ast.BinOp chain.\n\n    This will build a string from a series of ast.Constant nodes wrapped in\n    ast.BinOp nodes. Something like \"a\" + \"b\" + \"c\" or \"a %s\" % val etc.\n    The provided node can be any participant in the BinOp chain.\n\n    :param node: (ast.Constant or ast.BinOp) The node to process\n    :param stop: (ast.Constant or ast.BinOp) Optional base node to stop at\n    :returns: (Tuple) the root node of the expression, the string value\n    \"\"\"\n\n    def _get(node, bits, stop=None):\n        if node != stop:\n            bits.append(\n                _get(node.left, bits, stop)\n                if isinstance(node.left, ast.BinOp)\n                else node.left\n            )\n            bits.append(\n                _get(node.right, bits, stop)\n                if isinstance(node.right, ast.BinOp)\n                else node.right\n            )\n\n    bits = [node]\n    while isinstance(node._bandit_parent, ast.BinOp):\n        node = node._bandit_parent\n    if isinstance(node, ast.BinOp):\n        _get(node, bits, stop)\n    return (\n        node,\n        \" \".join(\n            [\n                x.value\n                for x in bits\n                if isinstance(x, ast.Constant) and isinstance(x.value, str)\n            ]\n        ),\n    )\n\n\ndef get_called_name(node):\n    \"\"\"Get a function name from an ast.Call node.\n\n    An ast.Call node representing a method call with present differently to one\n    wrapping a function call: thing.call() vs call(). This helper will grab the\n    unqualified call name correctly in either case.\n\n    :param node: (ast.Call) the call node\n    :returns: (String) the function name\n    \"\"\"\n    func = node.func\n    try:\n        return func.attr if isinstance(func, ast.Attribute) else func.id\n    except AttributeError:\n        return \"\"\n\n\ndef get_path_for_function(f):\n    \"\"\"Get the path of the file where the function is defined.\n\n    :returns: the path, or None if one could not be found or f is not a real\n        function\n    \"\"\"\n\n    if hasattr(f, \"__module__\"):\n        module_name = f.__module__\n    elif hasattr(f, \"im_func\"):\n        module_name = f.im_func.__module__\n    else:\n        LOG.warning(\"Cannot resolve file where %s is defined\", f)\n        return None\n\n    module = sys.modules[module_name]\n    if hasattr(module, \"__file__\"):\n        return module.__file__\n    else:\n        LOG.warning(\"Cannot resolve file path for module %s\", module_name)\n        return None\n\n\ndef parse_ini_file(f_loc):\n    config = configparser.ConfigParser()\n    try:\n        config.read(f_loc)\n        return {k: v for k, v in config.items(\"bandit\")}\n\n    except (configparser.Error, KeyError, TypeError):\n        LOG.warning(\n            \"Unable to parse config file %s or missing [bandit] \" \"section\",\n            f_loc,\n        )\n\n    return None\n\n\ndef check_ast_node(name):\n    \"Check if the given name is that of a valid AST node.\"\n    try:\n        # These ast Node types were deprecated in Python 3.12 and removed\n        # in Python 3.14, but plugins may still check on them.\n        if sys.version_info >= (3, 12) and name in (\n            \"Num\",\n            \"Str\",\n            \"Ellipsis\",\n            \"NameConstant\",\n            \"Bytes\",\n        ):\n            return name\n\n        node = getattr(ast, name)\n        if issubclass(node, ast.AST):\n            return name\n    except AttributeError:  # nosec(tkelsey): catching expected exception\n        pass\n\n    raise TypeError(f\"Error: {name} is not a valid node type in AST\")\n\n\ndef get_nosec(nosec_lines, context):\n    for lineno in context[\"linerange\"]:\n        nosec = nosec_lines.get(lineno, None)\n        if nosec is not None:\n            return nosec\n    return None\n"
  },
  {
    "path": "bandit/formatters/__init__.py",
    "content": ""
  },
  {
    "path": "bandit/formatters/csv.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=============\nCSV Formatter\n=============\n\nThis formatter outputs the issues in a comma separated values format.\n\n:Example:\n\n.. code-block:: none\n\n    filename,test_name,test_id,issue_severity,issue_confidence,issue_cwe,\n    issue_text,line_number,line_range,more_info\n    examples/yaml_load.py,blacklist_calls,B301,MEDIUM,HIGH,\n    https://cwe.mitre.org/data/definitions/20.html,\"Use of unsafe yaml\n    load. Allows instantiation of arbitrary objects. Consider yaml.safe_load().\n    \",5,[5],https://bandit.readthedocs.io/en/latest/\n\n.. versionadded:: 0.11.0\n\n.. versionchanged:: 1.5.0\n    New field `more_info` added to output\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\n# Necessary for this formatter to work when imported on Python 2. Importing\n# the standard library's csv module conflicts with the name of this module.\nimport csv\nimport logging\nimport sys\n\nfrom bandit.core import docs_utils\n\nLOG = logging.getLogger(__name__)\n\n\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Prints issues in CSV format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    results = manager.get_issue_list(\n        sev_level=sev_level, conf_level=conf_level\n    )\n\n    with fileobj:\n        fieldnames = [\n            \"filename\",\n            \"test_name\",\n            \"test_id\",\n            \"issue_severity\",\n            \"issue_confidence\",\n            \"issue_cwe\",\n            \"issue_text\",\n            \"line_number\",\n            \"col_offset\",\n            \"end_col_offset\",\n            \"line_range\",\n            \"more_info\",\n        ]\n\n        writer = csv.DictWriter(\n            fileobj, fieldnames=fieldnames, extrasaction=\"ignore\"\n        )\n        writer.writeheader()\n        for result in results:\n            r = result.as_dict(with_code=False)\n            r[\"issue_cwe\"] = r[\"issue_cwe\"][\"link\"]\n            r[\"more_info\"] = docs_utils.get_url(r[\"test_id\"])\n            writer.writerow(r)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"CSV output written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/formatters/custom.py",
    "content": "#\n# Copyright (c) 2017 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\n\"\"\"\n================\nCustom Formatter\n================\n\nThis formatter outputs the issues in custom machine-readable format.\n\ndefault template: ``{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}``\n\n:Example:\n\n.. code-block:: none\n\n    /usr/lib/python3.6/site-packages/openlp/core/utils/__init__.py:\\\n405: B310[bandit]: MEDIUM: Audit url open for permitted schemes. \\\nAllowing use of file:/ or custom schemes is often unexpected.\n\n.. versionadded:: 1.5.0\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\nimport logging\nimport os\nimport re\nimport string\nimport sys\n\nfrom bandit.core import test_properties\n\nLOG = logging.getLogger(__name__)\n\n\nclass SafeMapper(dict):\n    \"\"\"Safe mapper to handle format key errors\"\"\"\n\n    @classmethod  # To prevent PEP8 warnings in the test suite\n    def __missing__(cls, key):\n        return \"{%s}\" % key\n\n\n@test_properties.accepts_baseline\ndef report(manager, fileobj, sev_level, conf_level, template=None):\n    \"\"\"Prints issues in custom format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param template: Output template with non-terminal tags <N>\n                    (default: '{abspath}:{line}:\n                    {test_id}[bandit]: {severity}: {msg}')\n    \"\"\"\n\n    machine_output = {\"results\": [], \"errors\": []}\n    for fname, reason in manager.get_skipped():\n        machine_output[\"errors\"].append({\"filename\": fname, \"reason\": reason})\n\n    results = manager.get_issue_list(\n        sev_level=sev_level, conf_level=conf_level\n    )\n\n    msg_template = template\n    if template is None:\n        msg_template = \"{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}\"\n\n    # Dictionary of non-terminal tags that will be expanded\n    tag_mapper = {\n        \"abspath\": lambda issue: os.path.abspath(issue.fname),\n        \"relpath\": lambda issue: os.path.relpath(issue.fname),\n        \"line\": lambda issue: issue.lineno,\n        \"col\": lambda issue: issue.col_offset,\n        \"end_col\": lambda issue: issue.end_col_offset,\n        \"test_id\": lambda issue: issue.test_id,\n        \"severity\": lambda issue: issue.severity,\n        \"msg\": lambda issue: issue.text,\n        \"confidence\": lambda issue: issue.confidence,\n        \"range\": lambda issue: issue.linerange,\n        \"cwe\": lambda issue: issue.cwe,\n    }\n\n    # Create dictionary with tag sets to speed up search for similar tags\n    tag_sim_dict = {tag: set(tag) for tag, _ in tag_mapper.items()}\n\n    # Parse the format_string template and check the validity of tags\n    try:\n        parsed_template_orig = list(string.Formatter().parse(msg_template))\n        # of type (literal_text, field_name, fmt_spec, conversion)\n\n        # Check the format validity only, ignore keys\n        string.Formatter().vformat(msg_template, (), SafeMapper(line=0))\n    except ValueError as e:\n        LOG.error(\"Template is not in valid format: %s\", e.args[0])\n        sys.exit(2)\n\n    tag_set = {t[1] for t in parsed_template_orig if t[1] is not None}\n    if not tag_set:\n        LOG.error(\"No tags were found in the template. Are you missing '{}'?\")\n        sys.exit(2)\n\n    def get_similar_tag(tag):\n        similarity_list = [\n            (len(set(tag) & t_set), t) for t, t_set in tag_sim_dict.items()\n        ]\n        return sorted(similarity_list)[-1][1]\n\n    tag_blacklist = []\n    for tag in tag_set:\n        # check if the tag is in dictionary\n        if tag not in tag_mapper:\n            similar_tag = get_similar_tag(tag)\n            LOG.warning(\n                \"Tag '%s' was not recognized and will be skipped, \"\n                \"did you mean to use '%s'?\",\n                tag,\n                similar_tag,\n            )\n            tag_blacklist += [tag]\n\n    # Compose the message template back with the valid values only\n    msg_parsed_template_list = []\n    for literal_text, field_name, fmt_spec, conversion in parsed_template_orig:\n        if literal_text:\n            # if there is '{' or '}', double it to prevent expansion\n            literal_text = re.sub(\"{\", \"{{\", literal_text)\n            literal_text = re.sub(\"}\", \"}}\", literal_text)\n            msg_parsed_template_list.append(literal_text)\n\n        if field_name is not None:\n            if field_name in tag_blacklist:\n                msg_parsed_template_list.append(field_name)\n                continue\n            # Append the fmt_spec part\n            params = [field_name, fmt_spec, conversion]\n            markers = [\"\", \":\", \"!\"]\n            msg_parsed_template_list.append(\n                [\"{\"]\n                + [f\"{m + p}\" if p else \"\" for m, p in zip(markers, params)]\n                + [\"}\"]\n            )\n\n    msg_parsed_template = (\n        \"\".join([item for lst in msg_parsed_template_list for item in lst])\n        + \"\\n\"\n    )\n    with fileobj:\n        for defect in results:\n            evaluated_tags = SafeMapper(\n                (k, v(defect)) for k, v in tag_mapper.items()\n            )\n            output = msg_parsed_template.format(**evaluated_tags)\n\n            fileobj.write(output)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"Result written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/formatters/html.py",
    "content": "# Copyright (c) 2015 Rackspace, Inc.\n# Copyright (c) 2015 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==============\nHTML formatter\n==============\n\nThis formatter outputs the issues as HTML.\n\n:Example:\n\n.. code-block:: html\n\n    <!DOCTYPE html>\n    <html>\n    <head>\n\n    <meta charset=\"UTF-8\">\n\n    <title>\n        Bandit Report\n    </title>\n\n    <style>\n\n    html * {\n        font-family: \"Arial\", sans-serif;\n    }\n\n    pre {\n        font-family: \"Monaco\", monospace;\n    }\n\n    .bordered-box {\n        border: 1px solid black;\n        padding-top:.5em;\n        padding-bottom:.5em;\n        padding-left:1em;\n    }\n\n    .metrics-box {\n        font-size: 1.1em;\n        line-height: 130%;\n    }\n\n    .metrics-title {\n        font-size: 1.5em;\n        font-weight: 500;\n        margin-bottom: .25em;\n    }\n\n    .issue-description {\n        font-size: 1.3em;\n        font-weight: 500;\n    }\n\n    .candidate-issues {\n        margin-left: 2em;\n        border-left: solid 1px; LightGray;\n        padding-left: 5%;\n        margin-top: .2em;\n        margin-bottom: .2em;\n    }\n\n    .issue-block {\n        border: 1px solid LightGray;\n        padding-left: .5em;\n        padding-top: .5em;\n        padding-bottom: .5em;\n        margin-bottom: .5em;\n    }\n\n    .issue-sev-high {\n        background-color: Pink;\n    }\n\n    .issue-sev-medium {\n        background-color: NavajoWhite;\n    }\n\n    .issue-sev-low {\n        background-color: LightCyan;\n    }\n\n    </style>\n    </head>\n\n    <body>\n\n    <div id=\"metrics\">\n        <div class=\"metrics-box bordered-box\">\n            <div class=\"metrics-title\">\n                Metrics:<br>\n            </div>\n            Total lines of code: <span id=\"loc\">9</span><br>\n            Total lines skipped (#nosec): <span id=\"nosec\">0</span>\n        </div>\n    </div>\n\n\n\n\n    <br>\n    <div id=\"results\">\n\n    <div id=\"issue-0\">\n    <div class=\"issue-block issue-sev-medium\">\n        <b>yaml_load: </b> Use of unsafe yaml load. Allows\n        instantiation of arbitrary objects. Consider yaml.safe_load().<br>\n        <b>Test ID:</b> B506<br>\n        <b>Severity: </b>MEDIUM<br>\n        <b>Confidence: </b>HIGH<br>\n        <b>CWE: </b>CWE-20 (https://cwe.mitre.org/data/definitions/20.html)<br>\n        <b>File: </b><a href=\"examples/yaml_load.py\"\n        target=\"_blank\">examples/yaml_load.py</a> <br>\n        <b>More info: </b><a href=\"https://bandit.readthedocs.io/en/latest/\n        plugins/yaml_load.html\" target=\"_blank\">\n        https://bandit.readthedocs.io/en/latest/plugins/yaml_load.html</a>\n        <br>\n\n    <div class=\"code\">\n    <pre>\n    5       ystr = yaml.dump({'a' : 1, 'b' : 2, 'c' : 3})\n    6       y = yaml.load(ystr)\n    7       yaml.dump(y)\n    </pre>\n    </div>\n\n\n    </div>\n    </div>\n\n    </div>\n\n    </body>\n    </html>\n\n.. versionadded:: 0.14.0\n\n.. versionchanged:: 1.5.0\n    New field `more_info` added to output\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\nimport logging\nimport sys\nfrom html import escape as html_escape\n\nfrom bandit.core import docs_utils\nfrom bandit.core import test_properties\nfrom bandit.formatters import utils\n\nLOG = logging.getLogger(__name__)\n\n\n@test_properties.accepts_baseline\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Writes issues to 'fileobj' in HTML format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    header_block = \"\"\"\n<!DOCTYPE html>\n<html>\n<head>\n\n<meta charset=\"UTF-8\">\n\n<title>\n    Bandit Report\n</title>\n\n<style>\n\nhtml * {\n    font-family: \"Arial\", sans-serif;\n}\n\npre {\n    font-family: \"Monaco\", monospace;\n}\n\n.bordered-box {\n    border: 1px solid black;\n    padding-top:.5em;\n    padding-bottom:.5em;\n    padding-left:1em;\n}\n\n.metrics-box {\n    font-size: 1.1em;\n    line-height: 130%;\n}\n\n.metrics-title {\n    font-size: 1.5em;\n    font-weight: 500;\n    margin-bottom: .25em;\n}\n\n.issue-description {\n    font-size: 1.3em;\n    font-weight: 500;\n}\n\n.candidate-issues {\n    margin-left: 2em;\n    border-left: solid 1px; LightGray;\n    padding-left: 5%;\n    margin-top: .2em;\n    margin-bottom: .2em;\n}\n\n.issue-block {\n    border: 1px solid LightGray;\n    padding-left: .5em;\n    padding-top: .5em;\n    padding-bottom: .5em;\n    margin-bottom: .5em;\n}\n\n.issue-sev-high {\n    background-color: Pink;\n}\n\n.issue-sev-medium {\n    background-color: NavajoWhite;\n}\n\n.issue-sev-low {\n    background-color: LightCyan;\n}\n\n</style>\n</head>\n\"\"\"\n\n    report_block = \"\"\"\n<body>\n{metrics}\n{skipped}\n\n<br>\n<div id=\"results\">\n    {results}\n</div>\n\n</body>\n</html>\n\"\"\"\n\n    issue_block = \"\"\"\n<div id=\"issue-{issue_no}\">\n<div class=\"issue-block {issue_class}\">\n    <b>{test_name}: </b> {test_text}<br>\n    <b>Test ID:</b> {test_id}<br>\n    <b>Severity: </b>{severity}<br>\n    <b>Confidence: </b>{confidence}<br>\n    <b>CWE: </b><a href=\"{cwe_link}\" target=\"_blank\">CWE-{cwe.id}</a><br>\n    <b>File: </b><a href=\"{path}\" target=\"_blank\">{path}</a><br>\n    <b>Line number: </b>{line_number}<br>\n    <b>More info: </b><a href=\"{url}\" target=\"_blank\">{url}</a><br>\n{code}\n{candidates}\n</div>\n</div>\n\"\"\"\n\n    code_block = \"\"\"\n<div class=\"code\">\n<pre>\n{code}\n</pre>\n</div>\n\"\"\"\n\n    candidate_block = \"\"\"\n<div class=\"candidates\">\n<br>\n<b>Candidates: </b>\n{candidate_list}\n</div>\n\"\"\"\n\n    candidate_issue = \"\"\"\n<div class=\"candidate\">\n<div class=\"candidate-issues\">\n<pre>{code}</pre>\n</div>\n</div>\n\"\"\"\n\n    skipped_block = \"\"\"\n<br>\n<div id=\"skipped\">\n<div class=\"bordered-box\">\n<b>Skipped files:</b><br><br>\n{files_list}\n</div>\n</div>\n\"\"\"\n\n    metrics_block = \"\"\"\n<div id=\"metrics\">\n    <div class=\"metrics-box bordered-box\">\n        <div class=\"metrics-title\">\n            Metrics:<br>\n        </div>\n        Total lines of code: <span id=\"loc\">{loc}</span><br>\n        Total lines skipped (#nosec): <span id=\"nosec\">{nosec}</span>\n    </div>\n</div>\n\n\"\"\"\n\n    issues = manager.get_issue_list(sev_level=sev_level, conf_level=conf_level)\n\n    baseline = not isinstance(issues, list)\n\n    # build the skipped string to insert in the report\n    skipped_str = \"\".join(\n        f\"{fname} <b>reason:</b> {reason}<br>\"\n        for fname, reason in manager.get_skipped()\n    )\n    if skipped_str:\n        skipped_text = skipped_block.format(files_list=skipped_str)\n    else:\n        skipped_text = \"\"\n\n    # build the results string to insert in the report\n    results_str = \"\"\n    for index, issue in enumerate(issues):\n        if not baseline or len(issues[issue]) == 1:\n            candidates = \"\"\n            safe_code = html_escape(\n                issue.get_code(lines, True).strip(\"\\n\").lstrip(\" \")\n            )\n            code = code_block.format(code=safe_code)\n        else:\n            candidates_str = \"\"\n            code = \"\"\n            for candidate in issues[issue]:\n                candidate_code = html_escape(\n                    candidate.get_code(lines, True).strip(\"\\n\").lstrip(\" \")\n                )\n                candidates_str += candidate_issue.format(code=candidate_code)\n\n            candidates = candidate_block.format(candidate_list=candidates_str)\n\n        url = docs_utils.get_url(issue.test_id)\n        results_str += issue_block.format(\n            issue_no=index,\n            issue_class=f\"issue-sev-{issue.severity.lower()}\",\n            test_name=issue.test,\n            test_id=issue.test_id,\n            test_text=issue.text,\n            severity=issue.severity,\n            confidence=issue.confidence,\n            cwe=issue.cwe,\n            cwe_link=issue.cwe.link(),\n            path=issue.fname,\n            code=code,\n            candidates=candidates,\n            url=url,\n            line_number=issue.lineno,\n        )\n\n    # build the metrics string to insert in the report\n    metrics_summary = metrics_block.format(\n        loc=manager.metrics.data[\"_totals\"][\"loc\"],\n        nosec=manager.metrics.data[\"_totals\"][\"nosec\"],\n    )\n\n    # build the report and output it\n    report_contents = report_block.format(\n        metrics=metrics_summary, skipped=skipped_text, results=results_str\n    )\n\n    with fileobj:\n        wrapped_file = utils.wrap_file_object(fileobj)\n        wrapped_file.write(header_block)\n        wrapped_file.write(report_contents)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"HTML output written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/formatters/json.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==============\nJSON formatter\n==============\n\nThis formatter outputs the issues in JSON.\n\n:Example:\n\n.. code-block:: javascript\n\n    {\n      \"errors\": [],\n      \"generated_at\": \"2015-12-16T22:27:34Z\",\n      \"metrics\": {\n        \"_totals\": {\n          \"CONFIDENCE.HIGH\": 1,\n          \"CONFIDENCE.LOW\": 0,\n          \"CONFIDENCE.MEDIUM\": 0,\n          \"CONFIDENCE.UNDEFINED\": 0,\n          \"SEVERITY.HIGH\": 0,\n          \"SEVERITY.LOW\": 0,\n          \"SEVERITY.MEDIUM\": 1,\n          \"SEVERITY.UNDEFINED\": 0,\n          \"loc\": 5,\n          \"nosec\": 0\n        },\n        \"examples/yaml_load.py\": {\n          \"CONFIDENCE.HIGH\": 1,\n          \"CONFIDENCE.LOW\": 0,\n          \"CONFIDENCE.MEDIUM\": 0,\n          \"CONFIDENCE.UNDEFINED\": 0,\n          \"SEVERITY.HIGH\": 0,\n          \"SEVERITY.LOW\": 0,\n          \"SEVERITY.MEDIUM\": 1,\n          \"SEVERITY.UNDEFINED\": 0,\n          \"loc\": 5,\n          \"nosec\": 0\n        }\n      },\n      \"results\": [\n        {\n          \"code\": \"4     ystr = yaml.dump({'a' : 1, 'b' : 2, 'c' : 3})\\n5\n                         y = yaml.load(ystr)\\n6     yaml.dump(y)\\n\",\n          \"filename\": \"examples/yaml_load.py\",\n          \"issue_confidence\": \"HIGH\",\n          \"issue_severity\": \"MEDIUM\",\n          \"issue_cwe\": {\n            \"id\": 20,\n            \"link\": \"https://cwe.mitre.org/data/definitions/20.html\"\n          },\n          \"issue_text\": \"Use of unsafe yaml load. Allows instantiation of\n                         arbitrary objects. Consider yaml.safe_load().\\n\",\n          \"line_number\": 5,\n          \"line_range\": [\n            5\n          ],\n          \"more_info\": \"https://bandit.readthedocs.io/en/latest/\",\n          \"test_name\": \"blacklist_calls\",\n          \"test_id\": \"B301\"\n        }\n      ]\n    }\n\n.. versionadded:: 0.10.0\n\n.. versionchanged:: 1.5.0\n    New field `more_info` added to output\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\n# Necessary so we can import the standard library json module while continuing\n# to name this file json.py. (Python 2 only)\nimport datetime\nimport json\nimport logging\nimport operator\nimport sys\n\nfrom bandit.core import docs_utils\nfrom bandit.core import test_properties\n\nLOG = logging.getLogger(__name__)\n\n\n@test_properties.accepts_baseline\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"''Prints issues in JSON format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    machine_output = {\"results\": [], \"errors\": []}\n    for fname, reason in manager.get_skipped():\n        machine_output[\"errors\"].append({\"filename\": fname, \"reason\": reason})\n\n    results = manager.get_issue_list(\n        sev_level=sev_level, conf_level=conf_level\n    )\n\n    baseline = not isinstance(results, list)\n\n    if baseline:\n        collector = []\n        for r in results:\n            d = r.as_dict(max_lines=lines)\n            d[\"more_info\"] = docs_utils.get_url(d[\"test_id\"])\n            if len(results[r]) > 1:\n                d[\"candidates\"] = [\n                    c.as_dict(max_lines=lines) for c in results[r]\n                ]\n            collector.append(d)\n\n    else:\n        collector = [r.as_dict(max_lines=lines) for r in results]\n        for elem in collector:\n            elem[\"more_info\"] = docs_utils.get_url(elem[\"test_id\"])\n\n    itemgetter = operator.itemgetter\n    if manager.agg_type == \"vuln\":\n        machine_output[\"results\"] = sorted(\n            collector, key=itemgetter(\"test_name\")\n        )\n    else:\n        machine_output[\"results\"] = sorted(\n            collector, key=itemgetter(\"filename\")\n        )\n\n    machine_output[\"metrics\"] = manager.metrics.data\n\n    # timezone agnostic format\n    TS_FORMAT = \"%Y-%m-%dT%H:%M:%SZ\"\n\n    time_string = datetime.datetime.now(datetime.timezone.utc).strftime(\n        TS_FORMAT\n    )\n    machine_output[\"generated_at\"] = time_string\n\n    result = json.dumps(\n        machine_output, sort_keys=True, indent=2, separators=(\",\", \": \")\n    )\n\n    with fileobj:\n        fileobj.write(result)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"JSON output written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/formatters/sarif.py",
    "content": "# Copyright (c) Microsoft.  All Rights Reserved.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n# Note: this code mostly incorporated from\n# https://github.com/microsoft/bandit-sarif-formatter\n#\nr\"\"\"\n===============\nSARIF formatter\n===============\n\nThis formatter outputs the issues in SARIF formatted JSON.\n\n:Example:\n\n.. code-block:: javascript\n\n    {\n      \"runs\": [\n        {\n          \"tool\": {\n            \"driver\": {\n              \"name\": \"Bandit\",\n              \"organization\": \"PyCQA\",\n              \"rules\": [\n                {\n                  \"id\": \"B101\",\n                  \"name\": \"assert_used\",\n                  \"properties\": {\n                    \"tags\": [\n                      \"security\",\n                      \"external/cwe/cwe-703\"\n                    ],\n                    \"precision\": \"high\"\n                  },\n                  \"helpUri\": \"https://bandit.readthedocs.io/en/1.7.8/plugins/b101_assert_used.html\"\n                }\n              ],\n              \"version\": \"1.7.8\",\n              \"semanticVersion\": \"1.7.8\"\n            }\n          },\n          \"invocations\": [\n            {\n              \"executionSuccessful\": true,\n              \"endTimeUtc\": \"2024-03-05T03:28:48Z\"\n            }\n          ],\n          \"properties\": {\n            \"metrics\": {\n              \"_totals\": {\n                \"loc\": 1,\n                \"nosec\": 0,\n                \"skipped_tests\": 0,\n                \"SEVERITY.UNDEFINED\": 0,\n                \"CONFIDENCE.UNDEFINED\": 0,\n                \"SEVERITY.LOW\": 1,\n                \"CONFIDENCE.LOW\": 0,\n                \"SEVERITY.MEDIUM\": 0,\n                \"CONFIDENCE.MEDIUM\": 0,\n                \"SEVERITY.HIGH\": 0,\n                \"CONFIDENCE.HIGH\": 1\n              },\n              \"./examples/assert.py\": {\n                \"loc\": 1,\n                \"nosec\": 0,\n                \"skipped_tests\": 0,\n                \"SEVERITY.UNDEFINED\": 0,\n                \"SEVERITY.LOW\": 1,\n                \"SEVERITY.MEDIUM\": 0,\n                \"SEVERITY.HIGH\": 0,\n                \"CONFIDENCE.UNDEFINED\": 0,\n                \"CONFIDENCE.LOW\": 0,\n                \"CONFIDENCE.MEDIUM\": 0,\n                \"CONFIDENCE.HIGH\": 1\n              }\n            }\n          },\n          \"results\": [\n            {\n              \"message\": {\n                \"text\": \"Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.\"\n              },\n              \"level\": \"note\",\n              \"locations\": [\n                {\n                  \"physicalLocation\": {\n                    \"region\": {\n                      \"snippet\": {\n                        \"text\": \"assert True\\n\"\n                      },\n                      \"endColumn\": 11,\n                      \"endLine\": 1,\n                      \"startColumn\": 0,\n                      \"startLine\": 1\n                    },\n                    \"artifactLocation\": {\n                      \"uri\": \"examples/assert.py\"\n                    },\n                    \"contextRegion\": {\n                      \"snippet\": {\n                        \"text\": \"assert True\\n\"\n                      },\n                      \"endLine\": 1,\n                      \"startLine\": 1\n                    }\n                  }\n                }\n              ],\n              \"properties\": {\n                \"issue_confidence\": \"HIGH\",\n                \"issue_severity\": \"LOW\"\n              },\n              \"ruleId\": \"B101\",\n              \"ruleIndex\": 0\n            }\n          ]\n        }\n      ],\n      \"version\": \"2.1.0\",\n      \"$schema\": \"https://json.schemastore.org/sarif-2.1.0.json\"\n    }\n\n.. versionadded:: 1.7.8\n\n\"\"\"  # noqa: E501\nimport datetime\nimport logging\nimport pathlib\nimport sys\nimport urllib.parse as urlparse\n\nimport sarif_om as om\nfrom jschema_to_python.to_json import to_json\n\nimport bandit\nfrom bandit.core import docs_utils\n\nLOG = logging.getLogger(__name__)\nSCHEMA_URI = \"https://json.schemastore.org/sarif-2.1.0.json\"\nSCHEMA_VER = \"2.1.0\"\nTS_FORMAT = \"%Y-%m-%dT%H:%M:%SZ\"\n\n\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Prints issues in SARIF format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    log = om.SarifLog(\n        schema_uri=SCHEMA_URI,\n        version=SCHEMA_VER,\n        runs=[\n            om.Run(\n                tool=om.Tool(\n                    driver=om.ToolComponent(\n                        name=\"Bandit\",\n                        organization=bandit.__author__,\n                        semantic_version=bandit.__version__,\n                        version=bandit.__version__,\n                    )\n                ),\n                invocations=[\n                    om.Invocation(\n                        end_time_utc=datetime.datetime.now(\n                            datetime.timezone.utc\n                        ).strftime(TS_FORMAT),\n                        execution_successful=True,\n                    )\n                ],\n                properties={\"metrics\": manager.metrics.data},\n            )\n        ],\n    )\n\n    run = log.runs[0]\n    invocation = run.invocations[0]\n\n    skips = manager.get_skipped()\n    add_skipped_file_notifications(skips, invocation)\n\n    issues = manager.get_issue_list(sev_level=sev_level, conf_level=conf_level)\n\n    add_results(issues, run)\n\n    serializedLog = to_json(log)\n\n    with fileobj:\n        fileobj.write(serializedLog)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"SARIF output written to file: %s\", fileobj.name)\n\n\ndef add_skipped_file_notifications(skips, invocation):\n    if skips is None or len(skips) == 0:\n        return\n\n    if invocation.tool_configuration_notifications is None:\n        invocation.tool_configuration_notifications = []\n\n    for skip in skips:\n        (file_name, reason) = skip\n\n        notification = om.Notification(\n            level=\"error\",\n            message=om.Message(text=reason),\n            locations=[\n                om.Location(\n                    physical_location=om.PhysicalLocation(\n                        artifact_location=om.ArtifactLocation(\n                            uri=to_uri(file_name)\n                        )\n                    )\n                )\n            ],\n        )\n\n        invocation.tool_configuration_notifications.append(notification)\n\n\ndef add_results(issues, run):\n    if run.results is None:\n        run.results = []\n\n    rules = {}\n    rule_indices = {}\n    for issue in issues:\n        result = create_result(issue, rules, rule_indices)\n        run.results.append(result)\n\n    if len(rules) > 0:\n        run.tool.driver.rules = list(rules.values())\n\n\ndef create_result(issue, rules, rule_indices):\n    issue_dict = issue.as_dict()\n\n    rule, rule_index = create_or_find_rule(issue_dict, rules, rule_indices)\n\n    physical_location = om.PhysicalLocation(\n        artifact_location=om.ArtifactLocation(\n            uri=to_uri(issue_dict[\"filename\"])\n        )\n    )\n\n    add_region_and_context_region(\n        physical_location,\n        issue_dict[\"line_range\"],\n        issue_dict[\"col_offset\"],\n        issue_dict[\"end_col_offset\"],\n        issue_dict[\"code\"],\n    )\n\n    return om.Result(\n        rule_id=rule.id,\n        rule_index=rule_index,\n        message=om.Message(text=issue_dict[\"issue_text\"]),\n        level=level_from_severity(issue_dict[\"issue_severity\"]),\n        locations=[om.Location(physical_location=physical_location)],\n        properties={\n            \"issue_confidence\": issue_dict[\"issue_confidence\"],\n            \"issue_severity\": issue_dict[\"issue_severity\"],\n        },\n    )\n\n\ndef level_from_severity(severity):\n    if severity == \"HIGH\":\n        return \"error\"\n    elif severity == \"MEDIUM\":\n        return \"warning\"\n    elif severity == \"LOW\":\n        return \"note\"\n    else:\n        return \"warning\"\n\n\ndef add_region_and_context_region(\n    physical_location, line_range, col_offset, end_col_offset, code\n):\n    if code:\n        first_line_number, snippet_lines = parse_code(code)\n        snippet_line = snippet_lines[line_range[0] - first_line_number]\n        snippet = om.ArtifactContent(text=snippet_line)\n    else:\n        snippet = None\n\n    physical_location.region = om.Region(\n        start_line=line_range[0],\n        end_line=line_range[1] if len(line_range) > 1 else line_range[0],\n        start_column=col_offset + 1,\n        end_column=end_col_offset + 1,\n        snippet=snippet,\n    )\n\n    if code:\n        physical_location.context_region = om.Region(\n            start_line=first_line_number,\n            end_line=first_line_number + len(snippet_lines) - 1,\n            snippet=om.ArtifactContent(text=\"\".join(snippet_lines)),\n        )\n\n\ndef parse_code(code):\n    code_lines = code.split(\"\\n\")\n\n    # The last line from the split has nothing in it; it's an artifact of the\n    # last \"real\" line ending in a newline. Unless, of course, it doesn't:\n    last_line = code_lines[len(code_lines) - 1]\n\n    last_real_line_ends_in_newline = False\n    if len(last_line) == 0:\n        code_lines.pop()\n        last_real_line_ends_in_newline = True\n\n    snippet_lines = []\n    first_line_number = 0\n    first = True\n    for code_line in code_lines:\n        number_and_snippet_line = code_line.split(\" \", 1)\n        if first:\n            first_line_number = int(number_and_snippet_line[0])\n            first = False\n\n        snippet_line = number_and_snippet_line[1] + \"\\n\"\n        snippet_lines.append(snippet_line)\n\n    if not last_real_line_ends_in_newline:\n        last_line = snippet_lines[len(snippet_lines) - 1]\n        snippet_lines[len(snippet_lines) - 1] = last_line[: len(last_line) - 1]\n\n    return first_line_number, snippet_lines\n\n\ndef create_or_find_rule(issue_dict, rules, rule_indices):\n    rule_id = issue_dict[\"test_id\"]\n    if rule_id in rules:\n        return rules[rule_id], rule_indices[rule_id]\n\n    rule = om.ReportingDescriptor(\n        id=rule_id,\n        name=issue_dict[\"test_name\"],\n        help_uri=docs_utils.get_url(rule_id),\n        properties={\n            \"tags\": [\n                \"security\",\n                f\"external/cwe/cwe-{issue_dict['issue_cwe'].get('id')}\",\n            ],\n            \"precision\": issue_dict[\"issue_confidence\"].lower(),\n        },\n    )\n\n    index = len(rules)\n    rules[rule_id] = rule\n    rule_indices[rule_id] = index\n    return rule, index\n\n\ndef to_uri(file_path):\n    pure_path = pathlib.PurePath(file_path)\n    if pure_path.is_absolute():\n        return pure_path.as_uri()\n    else:\n        # Replace backslashes with slashes.\n        posix_path = pure_path.as_posix()\n        # %-encode special characters.\n        return urlparse.quote(posix_path)\n"
  },
  {
    "path": "bandit/formatters/screen.py",
    "content": "# Copyright (c) 2015 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n================\nScreen formatter\n================\n\nThis formatter outputs the issues as color coded text to screen.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B506: yaml_load] Use of unsafe yaml load. Allows\n       instantiation of arbitrary objects. Consider yaml.safe_load().\n\n       Severity: Medium   Confidence: High\n       CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html)\n       More Info: https://bandit.readthedocs.io/en/latest/\n       Location: examples/yaml_load.py:5\n    4       ystr = yaml.dump({'a' : 1, 'b' : 2, 'c' : 3})\n    5       y = yaml.load(ystr)\n    6       yaml.dump(y)\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.5.0\n    New field `more_info` added to output\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\nimport datetime\nimport logging\nimport sys\n\nfrom bandit.core import constants\nfrom bandit.core import docs_utils\nfrom bandit.core import test_properties\n\nIS_WIN_PLATFORM = sys.platform.startswith(\"win32\")\nCOLORAMA = False\n\n# This fixes terminal colors not displaying properly on Windows systems.\n# Colorama will intercept any ANSI escape codes and convert them to the\n# proper Windows console API calls to change text color.\nif IS_WIN_PLATFORM:\n    try:\n        import colorama\n    except ImportError:\n        pass\n    else:\n        COLORAMA = True\n\n\nLOG = logging.getLogger(__name__)\n\nCOLOR = {\n    \"DEFAULT\": \"\\033[0m\",\n    \"HEADER\": \"\\033[95m\",\n    \"LOW\": \"\\033[94m\",\n    \"MEDIUM\": \"\\033[93m\",\n    \"HIGH\": \"\\033[91m\",\n}\n\n\ndef header(text, *args):\n    return f\"{COLOR['HEADER']}{text % args}{COLOR['DEFAULT']}\"\n\n\ndef get_verbose_details(manager):\n    bits = []\n    bits.append(header(\"Files in scope (%i):\", len(manager.files_list)))\n    tpl = \"\\t%s (score: {SEVERITY: %i, CONFIDENCE: %i})\"\n    bits.extend(\n        [\n            tpl % (item, sum(score[\"SEVERITY\"]), sum(score[\"CONFIDENCE\"]))\n            for (item, score) in zip(manager.files_list, manager.scores)\n        ]\n    )\n    bits.append(header(\"Files excluded (%i):\", len(manager.excluded_files)))\n    bits.extend([f\"\\t{fname}\" for fname in manager.excluded_files])\n    return \"\\n\".join([str(bit) for bit in bits])\n\n\ndef get_metrics(manager):\n    bits = []\n    bits.append(header(\"\\nRun metrics:\"))\n    for criteria, _ in constants.CRITERIA:\n        bits.append(f\"\\tTotal issues (by {criteria.lower()}):\")\n        for rank in constants.RANKING:\n            bits.append(\n                \"\\t\\t%s: %s\"\n                % (\n                    rank.capitalize(),\n                    manager.metrics.data[\"_totals\"][f\"{criteria}.{rank}\"],\n                )\n            )\n    return \"\\n\".join([str(bit) for bit in bits])\n\n\ndef _output_issue_str(\n    issue, indent, show_lineno=True, show_code=True, lines=-1\n):\n    # returns a list of lines that should be added to the existing lines list\n    bits = []\n    bits.append(\n        \"%s%s>> Issue: [%s:%s] %s\"\n        % (\n            indent,\n            COLOR[issue.severity],\n            issue.test_id,\n            issue.test,\n            issue.text,\n        )\n    )\n\n    bits.append(\n        \"%s   Severity: %s   Confidence: %s\"\n        % (\n            indent,\n            issue.severity.capitalize(),\n            issue.confidence.capitalize(),\n        )\n    )\n\n    bits.append(f\"{indent}   CWE: {str(issue.cwe)}\")\n\n    bits.append(f\"{indent}   More Info: {docs_utils.get_url(issue.test_id)}\")\n\n    bits.append(\n        \"%s   Location: %s:%s:%s%s\"\n        % (\n            indent,\n            issue.fname,\n            issue.lineno if show_lineno else \"\",\n            issue.col_offset if show_lineno else \"\",\n            COLOR[\"DEFAULT\"],\n        )\n    )\n\n    if show_code:\n        bits.extend(\n            [indent + line for line in issue.get_code(lines, True).split(\"\\n\")]\n        )\n\n    return \"\\n\".join([bit for bit in bits])\n\n\ndef get_results(manager, sev_level, conf_level, lines):\n    bits = []\n    issues = manager.get_issue_list(sev_level, conf_level)\n    baseline = not isinstance(issues, list)\n    candidate_indent = \" \" * 10\n\n    if not len(issues):\n        return \"\\tNo issues identified.\"\n\n    for issue in issues:\n        # if not a baseline or only one candidate we know the issue\n        if not baseline or len(issues[issue]) == 1:\n            bits.append(_output_issue_str(issue, \"\", lines=lines))\n\n        # otherwise show the finding and the candidates\n        else:\n            bits.append(\n                _output_issue_str(\n                    issue, \"\", show_lineno=False, show_code=False\n                )\n            )\n\n            bits.append(\"\\n-- Candidate Issues --\")\n            for candidate in issues[issue]:\n                bits.append(\n                    _output_issue_str(candidate, candidate_indent, lines=lines)\n                )\n                bits.append(\"\\n\")\n        bits.append(\"-\" * 50)\n\n    return \"\\n\".join([bit for bit in bits])\n\n\ndef do_print(bits):\n    # needed so we can mock this stuff\n    print(\"\\n\".join([bit for bit in bits]))\n\n\n@test_properties.accepts_baseline\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Prints discovered issues formatted for screen reading\n\n    This makes use of VT100 terminal codes for colored text.\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    if IS_WIN_PLATFORM and COLORAMA:\n        colorama.init()\n\n    bits = []\n    if not manager.quiet or manager.results_count(sev_level, conf_level):\n        bits.append(\n            header(\n                \"Run started:%s\", datetime.datetime.now(datetime.timezone.utc)\n            )\n        )\n\n        if manager.verbose:\n            bits.append(get_verbose_details(manager))\n\n        bits.append(header(\"\\nTest results:\"))\n        bits.append(get_results(manager, sev_level, conf_level, lines))\n        bits.append(header(\"\\nCode scanned:\"))\n        bits.append(\n            \"\\tTotal lines of code: %i\"\n            % (manager.metrics.data[\"_totals\"][\"loc\"])\n        )\n\n        bits.append(\n            \"\\tTotal lines skipped (#nosec): %i\"\n            % (manager.metrics.data[\"_totals\"][\"nosec\"])\n        )\n\n        bits.append(get_metrics(manager))\n        skipped = manager.get_skipped()\n        bits.append(header(\"Files skipped (%i):\", len(skipped)))\n        bits.extend([\"\\t%s (%s)\" % skip for skip in skipped])\n        do_print(bits)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\n            \"Screen formatter output was not written to file: %s, \"\n            \"consider '-f txt'\",\n            fileobj.name,\n        )\n\n    if IS_WIN_PLATFORM and COLORAMA:\n        colorama.deinit()\n"
  },
  {
    "path": "bandit/formatters/text.py",
    "content": "# Copyright (c) 2015 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==============\nText Formatter\n==============\n\nThis formatter outputs the issues as plain text.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B301:blacklist_calls] Use of unsafe yaml load. Allows\n       instantiation of arbitrary objects. Consider yaml.safe_load().\n\n       Severity: Medium   Confidence: High\n       CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html)\n       More Info: https://bandit.readthedocs.io/en/latest/\n       Location: examples/yaml_load.py:5\n    4       ystr = yaml.dump({'a' : 1, 'b' : 2, 'c' : 3})\n    5       y = yaml.load(ystr)\n    6       yaml.dump(y)\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.5.0\n    New field `more_info` added to output\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\nimport datetime\nimport logging\nimport sys\n\nfrom bandit.core import constants\nfrom bandit.core import docs_utils\nfrom bandit.core import test_properties\nfrom bandit.formatters import utils\n\nLOG = logging.getLogger(__name__)\n\n\ndef get_verbose_details(manager):\n    bits = []\n    bits.append(f\"Files in scope ({len(manager.files_list)}):\")\n    tpl = \"\\t%s (score: {SEVERITY: %i, CONFIDENCE: %i})\"\n    bits.extend(\n        [\n            tpl % (item, sum(score[\"SEVERITY\"]), sum(score[\"CONFIDENCE\"]))\n            for (item, score) in zip(manager.files_list, manager.scores)\n        ]\n    )\n    bits.append(f\"Files excluded ({len(manager.excluded_files)}):\")\n    bits.extend([f\"\\t{fname}\" for fname in manager.excluded_files])\n    return \"\\n\".join([bit for bit in bits])\n\n\ndef get_metrics(manager):\n    bits = []\n    bits.append(\"\\nRun metrics:\")\n    for criteria, _ in constants.CRITERIA:\n        bits.append(f\"\\tTotal issues (by {criteria.lower()}):\")\n        for rank in constants.RANKING:\n            bits.append(\n                \"\\t\\t%s: %s\"\n                % (\n                    rank.capitalize(),\n                    manager.metrics.data[\"_totals\"][f\"{criteria}.{rank}\"],\n                )\n            )\n    return \"\\n\".join([bit for bit in bits])\n\n\ndef _output_issue_str(\n    issue, indent, show_lineno=True, show_code=True, lines=-1\n):\n    # returns a list of lines that should be added to the existing lines list\n    bits = []\n    bits.append(\n        f\"{indent}>> Issue: [{issue.test_id}:{issue.test}] {issue.text}\"\n    )\n\n    bits.append(\n        \"%s   Severity: %s   Confidence: %s\"\n        % (\n            indent,\n            issue.severity.capitalize(),\n            issue.confidence.capitalize(),\n        )\n    )\n\n    bits.append(f\"{indent}   CWE: {str(issue.cwe)}\")\n\n    bits.append(f\"{indent}   More Info: {docs_utils.get_url(issue.test_id)}\")\n\n    bits.append(\n        \"%s   Location: %s:%s:%s\"\n        % (\n            indent,\n            issue.fname,\n            issue.lineno if show_lineno else \"\",\n            issue.col_offset if show_lineno else \"\",\n        )\n    )\n\n    if show_code:\n        bits.extend(\n            [indent + line for line in issue.get_code(lines, True).split(\"\\n\")]\n        )\n\n    return \"\\n\".join([bit for bit in bits])\n\n\ndef get_results(manager, sev_level, conf_level, lines):\n    bits = []\n    issues = manager.get_issue_list(sev_level, conf_level)\n    baseline = not isinstance(issues, list)\n    candidate_indent = \" \" * 10\n\n    if not len(issues):\n        return \"\\tNo issues identified.\"\n\n    for issue in issues:\n        # if not a baseline or only one candidate we know the issue\n        if not baseline or len(issues[issue]) == 1:\n            bits.append(_output_issue_str(issue, \"\", lines=lines))\n\n        # otherwise show the finding and the candidates\n        else:\n            bits.append(\n                _output_issue_str(\n                    issue, \"\", show_lineno=False, show_code=False\n                )\n            )\n\n            bits.append(\"\\n-- Candidate Issues --\")\n            for candidate in issues[issue]:\n                bits.append(\n                    _output_issue_str(candidate, candidate_indent, lines=lines)\n                )\n                bits.append(\"\\n\")\n        bits.append(\"-\" * 50)\n    return \"\\n\".join([bit for bit in bits])\n\n\n@test_properties.accepts_baseline\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Prints discovered issues in the text format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    bits = []\n\n    if not manager.quiet or manager.results_count(sev_level, conf_level):\n        bits.append(\n            f\"Run started:{datetime.datetime.now(datetime.timezone.utc)}\"\n        )\n\n        if manager.verbose:\n            bits.append(get_verbose_details(manager))\n\n        bits.append(\"\\nTest results:\")\n        bits.append(get_results(manager, sev_level, conf_level, lines))\n        bits.append(\"\\nCode scanned:\")\n        bits.append(\n            \"\\tTotal lines of code: %i\"\n            % (manager.metrics.data[\"_totals\"][\"loc\"])\n        )\n\n        bits.append(\n            \"\\tTotal lines skipped (#nosec): %i\"\n            % (manager.metrics.data[\"_totals\"][\"nosec\"])\n        )\n        bits.append(\n            \"\\tTotal potential issues skipped due to specifically being \"\n            \"disabled (e.g., #nosec BXXX): %i\"\n            % (manager.metrics.data[\"_totals\"][\"skipped_tests\"])\n        )\n\n        skipped = manager.get_skipped()\n        bits.append(get_metrics(manager))\n        bits.append(f\"Files skipped ({len(skipped)}):\")\n        bits.extend([\"\\t%s (%s)\" % skip for skip in skipped])\n        result = \"\\n\".join([bit for bit in bits]) + \"\\n\"\n\n        with fileobj:\n            wrapped_file = utils.wrap_file_object(fileobj)\n            wrapped_file.write(result)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"Text output written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/formatters/utils.py",
    "content": "# Copyright (c) 2016 Rackspace, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\n\"\"\"Utility functions for formatting plugins for Bandit.\"\"\"\nimport io\n\n\ndef wrap_file_object(fileobj):\n    \"\"\"If the fileobj passed in cannot handle text, use TextIOWrapper\n    to handle the conversion.\n    \"\"\"\n    if isinstance(fileobj, io.TextIOBase):\n        return fileobj\n    return io.TextIOWrapper(fileobj)\n"
  },
  {
    "path": "bandit/formatters/xml.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=============\nXML Formatter\n=============\n\nThis formatter outputs the issues as XML.\n\n:Example:\n\n.. code-block:: xml\n\n    <?xml version='1.0' encoding='utf-8'?>\n    <testsuite name=\"bandit\" tests=\"1\"><testcase\n    classname=\"examples/yaml_load.py\" name=\"blacklist_calls\"><error\n    message=\"Use of unsafe yaml load. Allows instantiation of arbitrary\n    objects. Consider yaml.safe_load().&#10;\" type=\"MEDIUM\"\n    more_info=\"https://bandit.readthedocs.io/en/latest/\">Test ID: B301\n    Severity: MEDIUM Confidence: HIGH\n    CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html) Use of unsafe\n    yaml load.\n    Allows instantiation of arbitrary objects. Consider yaml.safe_load().\n\n    Location examples/yaml_load.py:5</error></testcase></testsuite>\n\n.. versionadded:: 0.12.0\n\n.. versionchanged:: 1.5.0\n    New field `more_info` added to output\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\nimport logging\nimport sys\nfrom xml.etree import ElementTree as ET  # nosec: B405\n\nfrom bandit.core import docs_utils\n\nLOG = logging.getLogger(__name__)\n\n\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Prints issues in XML format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    issues = manager.get_issue_list(sev_level=sev_level, conf_level=conf_level)\n    root = ET.Element(\"testsuite\", name=\"bandit\", tests=str(len(issues)))\n\n    for issue in issues:\n        test = issue.test\n        testcase = ET.SubElement(\n            root, \"testcase\", classname=issue.fname, name=test\n        )\n\n        text = (\n            \"Test ID: %s Severity: %s Confidence: %s\\nCWE: %s\\n%s\\n\"\n            \"Location %s:%s\"\n        )\n        text %= (\n            issue.test_id,\n            issue.severity,\n            issue.confidence,\n            issue.cwe,\n            issue.text,\n            issue.fname,\n            issue.lineno,\n        )\n        ET.SubElement(\n            testcase,\n            \"error\",\n            more_info=docs_utils.get_url(issue.test_id),\n            type=issue.severity,\n            message=issue.text,\n        ).text = text\n\n    tree = ET.ElementTree(root)\n\n    if fileobj.name == sys.stdout.name:\n        fileobj = sys.stdout.buffer\n    elif fileobj.mode == \"w\":\n        fileobj.close()\n        fileobj = open(fileobj.name, \"wb\")\n\n    with fileobj:\n        tree.write(fileobj, encoding=\"utf-8\", xml_declaration=True)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"XML output written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/formatters/yaml.py",
    "content": "# Copyright (c) 2017 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==============\nYAML Formatter\n==============\n\nThis formatter outputs the issues in a yaml format.\n\n:Example:\n\n.. code-block:: none\n\n    errors: []\n    generated_at: '2017-03-09T22:29:30Z'\n    metrics:\n      _totals:\n        CONFIDENCE.HIGH: 1\n        CONFIDENCE.LOW: 0\n        CONFIDENCE.MEDIUM: 0\n        CONFIDENCE.UNDEFINED: 0\n        SEVERITY.HIGH: 0\n        SEVERITY.LOW: 0\n        SEVERITY.MEDIUM: 1\n        SEVERITY.UNDEFINED: 0\n        loc: 9\n        nosec: 0\n      examples/yaml_load.py:\n        CONFIDENCE.HIGH: 1\n        CONFIDENCE.LOW: 0\n        CONFIDENCE.MEDIUM: 0\n        CONFIDENCE.UNDEFINED: 0\n        SEVERITY.HIGH: 0\n        SEVERITY.LOW: 0\n        SEVERITY.MEDIUM: 1\n        SEVERITY.UNDEFINED: 0\n        loc: 9\n        nosec: 0\n    results:\n    - code: '5     ystr = yaml.dump({''a'' : 1, ''b'' : 2, ''c'' : 3})\\n\n             6     y = yaml.load(ystr)\\n7     yaml.dump(y)\\n'\n      filename: examples/yaml_load.py\n      issue_confidence: HIGH\n      issue_severity: MEDIUM\n      issue_text: Use of unsafe yaml load. Allows instantiation of arbitrary\n                  objects.\n        Consider yaml.safe_load().\n      line_number: 6\n      line_range:\n      - 6\n      more_info: https://bandit.readthedocs.io/en/latest/\n      test_id: B506\n      test_name: yaml_load\n\n.. versionadded:: 1.5.0\n\n.. versionchanged:: 1.7.3\n    New field `CWE` added to output\n\n\"\"\"\n# Necessary for this formatter to work when imported on Python 2. Importing\n# the standard library's yaml module conflicts with the name of this module.\nimport datetime\nimport logging\nimport operator\nimport sys\n\nimport yaml\n\nfrom bandit.core import docs_utils\n\nLOG = logging.getLogger(__name__)\n\n\ndef report(manager, fileobj, sev_level, conf_level, lines=-1):\n    \"\"\"Prints issues in YAML format\n\n    :param manager: the bandit manager object\n    :param fileobj: The output file object, which may be sys.stdout\n    :param sev_level: Filtering severity level\n    :param conf_level: Filtering confidence level\n    :param lines: Number of lines to report, -1 for all\n    \"\"\"\n\n    machine_output = {\"results\": [], \"errors\": []}\n    for fname, reason in manager.get_skipped():\n        machine_output[\"errors\"].append({\"filename\": fname, \"reason\": reason})\n\n    results = manager.get_issue_list(\n        sev_level=sev_level, conf_level=conf_level\n    )\n\n    collector = [r.as_dict(max_lines=lines) for r in results]\n    for elem in collector:\n        elem[\"more_info\"] = docs_utils.get_url(elem[\"test_id\"])\n\n    itemgetter = operator.itemgetter\n    if manager.agg_type == \"vuln\":\n        machine_output[\"results\"] = sorted(\n            collector, key=itemgetter(\"test_name\")\n        )\n    else:\n        machine_output[\"results\"] = sorted(\n            collector, key=itemgetter(\"filename\")\n        )\n\n    machine_output[\"metrics\"] = manager.metrics.data\n\n    for result in machine_output[\"results\"]:\n        if \"code\" in result:\n            code = result[\"code\"].replace(\"\\n\", \"\\\\n\")\n            result[\"code\"] = code\n\n    # timezone agnostic format\n    TS_FORMAT = \"%Y-%m-%dT%H:%M:%SZ\"\n\n    time_string = datetime.datetime.now(datetime.timezone.utc).strftime(\n        TS_FORMAT\n    )\n    machine_output[\"generated_at\"] = time_string\n\n    yaml.safe_dump(machine_output, fileobj, default_flow_style=False)\n\n    if fileobj.name != sys.stdout.name:\n        LOG.info(\"YAML output written to file: %s\", fileobj.name)\n"
  },
  {
    "path": "bandit/plugins/__init__.py",
    "content": ""
  },
  {
    "path": "bandit/plugins/app_debug.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n======================================================\nB201: Test for use of flask app with debug set to true\n======================================================\n\nRunning Flask applications in debug mode results in the Werkzeug debugger\nbeing enabled. This includes a feature that allows arbitrary code execution.\nDocumentation for both Flask [1]_ and Werkzeug [2]_ strongly suggests that\ndebug mode should never be enabled on production systems.\n\nOperating a production server with debug mode enabled was the probable cause\nof the Patreon breach in 2015 [3]_.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: A Flask app appears to be run with debug=True, which exposes\n    the Werkzeug debugger and allows the execution of arbitrary code.\n       Severity: High   Confidence: High\n       CWE: CWE-94 (https://cwe.mitre.org/data/definitions/94.html)\n       Location: examples/flask_debug.py:10\n    9 #bad\n    10    app.run(debug=True)\n    11\n\n.. seealso::\n\n .. [1] https://flask.palletsprojects.com/en/1.1.x/quickstart/#debug-mode\n .. [2] https://werkzeug.palletsprojects.com/en/1.0.x/debug/\n .. [3] https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/\n .. https://cwe.mitre.org/data/definitions/94.html\n\n.. versionadded:: 0.15.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"  # noqa: E501\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.test_id(\"B201\")\n@test.checks(\"Call\")\ndef flask_debug_true(context):\n    if context.is_module_imported_like(\"flask\"):\n        if context.call_function_name_qual.endswith(\".run\"):\n            if context.check_call_arg_value(\"debug\", \"True\"):\n                return bandit.Issue(\n                    severity=bandit.HIGH,\n                    confidence=bandit.MEDIUM,\n                    cwe=issue.Cwe.CODE_INJECTION,\n                    text=\"A Flask app appears to be run with debug=True, \"\n                    \"which exposes the Werkzeug debugger and allows \"\n                    \"the execution of arbitrary code.\",\n                    lineno=context.get_lineno_for_call_arg(\"debug\"),\n                )\n"
  },
  {
    "path": "bandit/plugins/asserts.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n============================\nB101: Test for use of assert\n============================\n\nThis plugin test checks for the use of the Python ``assert`` keyword. It was\ndiscovered that some projects used assert to enforce interface constraints.\nHowever, assert is removed with compiling to optimised byte code (`python -O`\nproducing \\*.opt-1.pyc files). This caused various protections to be removed.\nConsider raising a semantically meaningful error or ``AssertionError`` instead.\n\nPlease see\nhttps://docs.python.org/3/reference/simple_stmts.html#the-assert-statement for\nmore info on ``assert``.\n\n**Config Options:**\n\nYou can configure files that skip this check. This is often useful when you\nuse assert statements in test cases.\n\n.. code-block:: yaml\n\n    assert_used:\n      skips: ['*_test.py', '*test_*.py']\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Use of assert detected. The enclosed code will be removed when\n       compiling to optimised byte code.\n       Severity: Low   Confidence: High\n       CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)\n       Location: ./examples/assert.py:1\n    1 assert logged_in\n    2 display_assets()\n\n.. seealso::\n\n - https://bugs.launchpad.net/juniperopenstack/+bug/1456193\n - https://bugs.launchpad.net/heat/+bug/1397883\n - https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement\n - https://cwe.mitre.org/data/definitions/703.html\n\n.. versionadded:: 0.11.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport fnmatch\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef gen_config(name):\n    if name == \"assert_used\":\n        return {\"skips\": []}\n\n\n@test.takes_config\n@test.test_id(\"B101\")\n@test.checks(\"Assert\")\ndef assert_used(context, config):\n    for skip in config.get(\"skips\", []):\n        if fnmatch.fnmatch(context.filename, skip):\n            return None\n\n    return bandit.Issue(\n        severity=bandit.LOW,\n        confidence=bandit.HIGH,\n        cwe=issue.Cwe.IMPROPER_CHECK_OF_EXCEPT_COND,\n        text=(\n            \"Use of assert detected. The enclosed code \"\n            \"will be removed when compiling to optimised byte code.\"\n        ),\n    )\n"
  },
  {
    "path": "bandit/plugins/crypto_request_no_cert_validation.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=============================================\nB501: Test for missing certificate validation\n=============================================\n\nEncryption in general is typically critical to the security of many\napplications.  Using TLS can greatly increase security by guaranteeing the\nidentity of the party you are communicating with.  This is accomplished by one\nor both parties presenting trusted certificates during the connection\ninitialization phase of TLS.\n\nWhen HTTPS request methods are used, certificates are validated automatically\nwhich is the desired behavior.  If certificate validation is explicitly turned\noff Bandit will return a HIGH severity error.\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [request_with_no_cert_validation] Call to requests with\n    verify=False disabling SSL certificate checks, security issue.\n       Severity: High   Confidence: High\n       CWE: CWE-295 (https://cwe.mitre.org/data/definitions/295.html)\n       Location: examples/requests-ssl-verify-disabled.py:4\n    3   requests.get('https://gmail.com', verify=True)\n    4   requests.get('https://gmail.com', verify=False)\n    5   requests.post('https://gmail.com', verify=True)\n\n.. seealso::\n\n - https://security.openstack.org/guidelines/dg_move-data-securely.html\n - https://security.openstack.org/guidelines/dg_validate-certificates.html\n - https://cwe.mitre.org/data/definitions/295.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n.. versionchanged:: 1.7.5\n    Added check for httpx module\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B501\")\ndef request_with_no_cert_validation(context):\n    HTTP_VERBS = {\"get\", \"options\", \"head\", \"post\", \"put\", \"patch\", \"delete\"}\n    HTTPX_ATTRS = {\"request\", \"stream\", \"Client\", \"AsyncClient\"} | HTTP_VERBS\n    qualname = context.call_function_name_qual.split(\".\")[0]\n\n    if (\n        qualname == \"requests\"\n        and context.call_function_name in HTTP_VERBS\n        or qualname == \"httpx\"\n        and context.call_function_name in HTTPX_ATTRS\n    ):\n        if context.check_call_arg_value(\"verify\", \"False\"):\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.IMPROPER_CERT_VALIDATION,\n                text=f\"Call to {qualname} with verify=False disabling SSL \"\n                \"certificate checks, security issue.\",\n                lineno=context.get_lineno_for_call_arg(\"verify\"),\n            )\n"
  },
  {
    "path": "bandit/plugins/django_sql_injection.py",
    "content": "#\n# Copyright (C) 2018 [Victor Torre](https://github.com/ehooo)\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef keywords2dict(keywords):\n    kwargs = {}\n    for node in keywords:\n        if isinstance(node, ast.keyword):\n            kwargs[node.arg] = node.value\n    return kwargs\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B610\")\ndef django_extra_used(context):\n    \"\"\"**B610: Potential SQL injection on extra function**\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B610:django_extra_used] Use of extra potential SQL attack vector.\n           Severity: Medium Confidence: Medium\n           CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)\n           Location: examples/django_sql_injection_extra.py:29:0\n           More Info: https://bandit.readthedocs.io/en/latest/plugins/b610_django_extra_used.html\n        28  tables_str = 'django_content_type\" WHERE \"auth_user\".\"username\"=\"admin'\n        29  User.objects.all().extra(tables=[tables_str]).distinct()\n\n    .. seealso::\n\n     - https://docs.djangoproject.com/en/dev/topics/security/\\\n#sql-injection-protection\n     - https://cwe.mitre.org/data/definitions/89.html\n\n    .. versionadded:: 1.5.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    description = \"Use of extra potential SQL attack vector.\"\n    if context.call_function_name == \"extra\":\n        kwargs = keywords2dict(context.node.keywords)\n        args = context.node.args\n        if args:\n            if len(args) >= 1:\n                kwargs[\"select\"] = args[0]\n            if len(args) >= 2:\n                kwargs[\"where\"] = args[1]\n            if len(args) >= 3:\n                kwargs[\"params\"] = args[2]\n            if len(args) >= 4:\n                kwargs[\"tables\"] = args[3]\n            if len(args) >= 5:\n                kwargs[\"order_by\"] = args[4]\n            if len(args) >= 6:\n                kwargs[\"select_params\"] = args[5]\n        insecure = False\n        for key in [\"where\", \"tables\"]:\n            if key in kwargs:\n                if isinstance(kwargs[key], ast.List):\n                    for val in kwargs[key].elts:\n                        if not (\n                            isinstance(val, ast.Constant)\n                            and isinstance(val.value, str)\n                        ):\n                            insecure = True\n                            break\n                else:\n                    insecure = True\n                    break\n        if not insecure and \"select\" in kwargs:\n            if isinstance(kwargs[\"select\"], ast.Dict):\n                for k in kwargs[\"select\"].keys:\n                    if not (\n                        isinstance(k, ast.Constant)\n                        and isinstance(k.value, str)\n                    ):\n                        insecure = True\n                        break\n                if not insecure:\n                    for v in kwargs[\"select\"].values:\n                        if not (\n                            isinstance(v, ast.Constant)\n                            and isinstance(v.value, str)\n                        ):\n                            insecure = True\n                            break\n            else:\n                insecure = True\n\n        if insecure:\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.MEDIUM,\n                cwe=issue.Cwe.SQL_INJECTION,\n                text=description,\n            )\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B611\")\ndef django_rawsql_used(context):\n    \"\"\"**B611: Potential SQL injection on RawSQL function**\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B611:django_rawsql_used] Use of RawSQL potential SQL attack vector.\n           Severity: Medium Confidence: Medium\n           CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)\n           Location: examples/django_sql_injection_raw.py:11:26\n           More Info: https://bandit.readthedocs.io/en/latest/plugins/b611_django_rawsql_used.html\n        10        ' WHERE \"username\"=\"admin\" OR 1=%s --'\n        11  User.objects.annotate(val=RawSQL(raw, [0]))\n\n    .. seealso::\n\n     - https://docs.djangoproject.com/en/dev/topics/security/\\\n#sql-injection-protection\n     - https://cwe.mitre.org/data/definitions/89.html\n\n    .. versionadded:: 1.5.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    description = \"Use of RawSQL potential SQL attack vector.\"\n    if context.is_module_imported_like(\"django.db.models\"):\n        if context.call_function_name == \"RawSQL\":\n            if context.node.args:\n                sql = context.node.args[0]\n            else:\n                kwargs = keywords2dict(context.node.keywords)\n                sql = kwargs[\"sql\"]\n\n            if not (\n                isinstance(sql, ast.Constant) and isinstance(sql.value, str)\n            ):\n                return bandit.Issue(\n                    severity=bandit.MEDIUM,\n                    confidence=bandit.MEDIUM,\n                    cwe=issue.Cwe.SQL_INJECTION,\n                    text=description,\n                )\n"
  },
  {
    "path": "bandit/plugins/django_xss.py",
    "content": "#\n# Copyright 2018 Victor Torre\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\nclass DeepAssignation:\n    def __init__(self, var_name, ignore_nodes=None):\n        self.var_name = var_name\n        self.ignore_nodes = ignore_nodes\n\n    def is_assigned_in(self, items):\n        assigned = []\n        for ast_inst in items:\n            new_assigned = self.is_assigned(ast_inst)\n            if new_assigned:\n                if isinstance(new_assigned, (list, tuple)):\n                    assigned.extend(new_assigned)\n                else:\n                    assigned.append(new_assigned)\n        return assigned\n\n    def is_assigned(self, node):\n        assigned = False\n        if self.ignore_nodes:\n            if isinstance(self.ignore_nodes, (list, tuple, object)):\n                if isinstance(node, self.ignore_nodes):\n                    return assigned\n\n        if isinstance(node, ast.Expr):\n            assigned = self.is_assigned(node.value)\n        elif isinstance(node, ast.FunctionDef):\n            for name in node.args.args:\n                if isinstance(name, ast.Name):\n                    if name.id == self.var_name.id:\n                        # If is param the assignations are not affected\n                        return assigned\n            assigned = self.is_assigned_in(node.body)\n        elif isinstance(node, ast.With):\n            for withitem in node.items:\n                var_id = getattr(withitem.optional_vars, \"id\", None)\n                if var_id == self.var_name.id:\n                    assigned = node\n                else:\n                    assigned = self.is_assigned_in(node.body)\n        elif isinstance(node, ast.Try):\n            assigned = []\n            assigned.extend(self.is_assigned_in(node.body))\n            assigned.extend(self.is_assigned_in(node.handlers))\n            assigned.extend(self.is_assigned_in(node.orelse))\n            assigned.extend(self.is_assigned_in(node.finalbody))\n        elif isinstance(node, ast.ExceptHandler):\n            assigned = []\n            assigned.extend(self.is_assigned_in(node.body))\n        elif isinstance(node, (ast.If, ast.For, ast.While)):\n            assigned = []\n            assigned.extend(self.is_assigned_in(node.body))\n            assigned.extend(self.is_assigned_in(node.orelse))\n        elif isinstance(node, ast.AugAssign):\n            if isinstance(node.target, ast.Name):\n                if node.target.id == self.var_name.id:\n                    assigned = node.value\n        elif isinstance(node, ast.Assign) and node.targets:\n            target = node.targets[0]\n            if isinstance(target, ast.Name):\n                if target.id == self.var_name.id:\n                    assigned = node.value\n            elif isinstance(target, ast.Tuple) and isinstance(\n                node.value, ast.Tuple\n            ):\n                pos = 0\n                for name in target.elts:\n                    if name.id == self.var_name.id:\n                        assigned = node.value.elts[pos]\n                        break\n                    pos += 1\n        return assigned\n\n\ndef evaluate_var(xss_var, parent, until, ignore_nodes=None):\n    secure = False\n    if isinstance(xss_var, ast.Name):\n        if isinstance(parent, ast.FunctionDef):\n            for name in parent.args.args:\n                if name.arg == xss_var.id:\n                    return False  # Params are not secure\n\n        analyser = DeepAssignation(xss_var, ignore_nodes)\n        for node in parent.body:\n            if node.lineno >= until:\n                break\n            to = analyser.is_assigned(node)\n            if to:\n                if isinstance(to, ast.Constant) and isinstance(to.value, str):\n                    secure = True\n                elif isinstance(to, ast.Name):\n                    secure = evaluate_var(to, parent, to.lineno, ignore_nodes)\n                elif isinstance(to, ast.Call):\n                    secure = evaluate_call(to, parent, ignore_nodes)\n                elif isinstance(to, (list, tuple)):\n                    num_secure = 0\n                    for some_to in to:\n                        if isinstance(some_to, ast.Constant) and isinstance(\n                            some_to.value, str\n                        ):\n                            num_secure += 1\n                        elif isinstance(some_to, ast.Name):\n                            if evaluate_var(\n                                some_to, parent, node.lineno, ignore_nodes\n                            ):\n                                num_secure += 1\n                            else:\n                                break\n                        else:\n                            break\n                    if num_secure == len(to):\n                        secure = True\n                    else:\n                        secure = False\n                        break\n                else:\n                    secure = False\n                    break\n    return secure\n\n\ndef evaluate_call(call, parent, ignore_nodes=None):\n    secure = False\n    evaluate = False\n    if isinstance(call, ast.Call) and isinstance(call.func, ast.Attribute):\n        if (\n            isinstance(call.func.value, ast.Constant)\n            and call.func.attr == \"format\"\n        ):\n            evaluate = True\n            if call.keywords:\n                evaluate = False  # TODO(??) get support for this\n\n    if evaluate:\n        args = list(call.args)\n        num_secure = 0\n        for arg in args:\n            if isinstance(arg, ast.Constant) and isinstance(arg.value, str):\n                num_secure += 1\n            elif isinstance(arg, ast.Name):\n                if evaluate_var(arg, parent, call.lineno, ignore_nodes):\n                    num_secure += 1\n                else:\n                    break\n            elif isinstance(arg, ast.Call):\n                if evaluate_call(arg, parent, ignore_nodes):\n                    num_secure += 1\n                else:\n                    break\n            elif isinstance(arg, ast.Starred) and isinstance(\n                arg.value, (ast.List, ast.Tuple)\n            ):\n                args.extend(arg.value.elts)\n                num_secure += 1\n            else:\n                break\n        secure = num_secure == len(args)\n\n    return secure\n\n\ndef transform2call(var):\n    if isinstance(var, ast.BinOp):\n        is_mod = isinstance(var.op, ast.Mod)\n        is_left_str = isinstance(var.left, ast.Constant) and isinstance(\n            var.left.value, str\n        )\n        if is_mod and is_left_str:\n            new_call = ast.Call()\n            new_call.args = []\n            new_call.args = []\n            new_call.keywords = None\n            new_call.lineno = var.lineno\n            new_call.func = ast.Attribute()\n            new_call.func.value = var.left\n            new_call.func.attr = \"format\"\n            if isinstance(var.right, ast.Tuple):\n                new_call.args = var.right.elts\n            else:\n                new_call.args = [var.right]\n            return new_call\n\n\ndef check_risk(node):\n    description = \"Potential XSS on mark_safe function.\"\n    xss_var = node.args[0]\n\n    secure = False\n\n    if isinstance(xss_var, ast.Name):\n        # Check if the var are secure\n        parent = node._bandit_parent\n        while not isinstance(parent, (ast.Module, ast.FunctionDef)):\n            parent = parent._bandit_parent\n\n        is_param = False\n        if isinstance(parent, ast.FunctionDef):\n            for name in parent.args.args:\n                if name.arg == xss_var.id:\n                    is_param = True\n                    break\n\n        if not is_param:\n            secure = evaluate_var(xss_var, parent, node.lineno)\n    elif isinstance(xss_var, ast.Call):\n        parent = node._bandit_parent\n        while not isinstance(parent, (ast.Module, ast.FunctionDef)):\n            parent = parent._bandit_parent\n        secure = evaluate_call(xss_var, parent)\n    elif isinstance(xss_var, ast.BinOp):\n        is_mod = isinstance(xss_var.op, ast.Mod)\n        is_left_str = isinstance(xss_var.left, ast.Constant) and isinstance(\n            xss_var.left.value, str\n        )\n        if is_mod and is_left_str:\n            parent = node._bandit_parent\n            while not isinstance(parent, (ast.Module, ast.FunctionDef)):\n                parent = parent._bandit_parent\n            new_call = transform2call(xss_var)\n            secure = evaluate_call(new_call, parent)\n\n    if not secure:\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.HIGH,\n            cwe=issue.Cwe.BASIC_XSS,\n            text=description,\n        )\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B703\")\ndef django_mark_safe(context):\n    \"\"\"**B703: Potential XSS on mark_safe function**\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B703:django_mark_safe] Potential XSS on mark_safe function.\n           Severity: Medium Confidence: High\n           CWE: CWE-80 (https://cwe.mitre.org/data/definitions/80.html)\n           Location: examples/mark_safe_insecure.py:159:4\n           More Info: https://bandit.readthedocs.io/en/latest/plugins/b703_django_mark_safe.html\n        158         str_arg = 'could be insecure'\n        159     safestring.mark_safe(str_arg)\n\n    .. seealso::\n\n     - https://docs.djangoproject.com/en/dev/topics/security/\\\n#cross-site-scripting-xss-protection\n     - https://docs.djangoproject.com/en/dev/ref/utils/\\\n#module-django.utils.safestring\n     - https://docs.djangoproject.com/en/dev/ref/utils/\\\n#django.utils.html.format_html\n     - https://cwe.mitre.org/data/definitions/80.html\n\n    .. versionadded:: 1.5.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    if context.is_module_imported_like(\"django.utils.safestring\"):\n        affected_functions = [\n            \"mark_safe\",\n            \"SafeText\",\n            \"SafeUnicode\",\n            \"SafeString\",\n            \"SafeBytes\",\n        ]\n        if context.call_function_name in affected_functions:\n            xss = context.node.args[0]\n            if not (\n                isinstance(xss, ast.Constant) and isinstance(xss.value, str)\n            ):\n                return check_risk(context.node)\n"
  },
  {
    "path": "bandit/plugins/exec.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==============================\nB102: Test for the use of exec\n==============================\n\nThis plugin test checks for the use of Python's `exec` method or keyword. The\nPython docs succinctly describe why the use of `exec` is risky.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Use of exec detected.\n       Severity: Medium   Confidence: High\n       CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n       Location: ./examples/exec.py:2\n    1 exec(\"do evil\")\n\n\n.. seealso::\n\n - https://docs.python.org/3/library/functions.html#exec\n - https://www.python.org/dev/peps/pep-0551/#background\n - https://www.python.org/dev/peps/pep-0578/#suggested-audit-hook-locations\n - https://cwe.mitre.org/data/definitions/78.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef exec_issue():\n    return bandit.Issue(\n        severity=bandit.MEDIUM,\n        confidence=bandit.HIGH,\n        cwe=issue.Cwe.OS_COMMAND_INJECTION,\n        text=\"Use of exec detected.\",\n    )\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B102\")\ndef exec_used(context):\n    if context.call_function_name_qual == \"exec\":\n        return exec_issue()\n"
  },
  {
    "path": "bandit/plugins/general_bad_file_permissions.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==================================================\nB103: Test for setting permissive file permissions\n==================================================\n\nPOSIX based operating systems utilize a permissions model to protect access to\nparts of the file system. This model supports three roles \"owner\", \"group\"\nand \"world\" each role may have a combination of \"read\", \"write\" or \"execute\"\nflags sets. Python provides ``chmod`` to manipulate POSIX style permissions.\n\nThis plugin test looks for the use of ``chmod`` and will alert when it is used\nto set particularly permissive control flags. A MEDIUM warning is generated if\na file is set to group write or executable and a HIGH warning is reported if a\nfile is set world write or executable. Warnings are given with HIGH confidence.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Probable insecure usage of temp file/directory.\n       Severity: Medium   Confidence: Medium\n       CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html)\n       Location: ./examples/os-chmod.py:15\n    14  os.chmod('/etc/hosts', 0o777)\n    15  os.chmod('/tmp/oh_hai', 0x1ff)\n    16  os.chmod('/etc/passwd', stat.S_IRWXU)\n\n    >> Issue: Chmod setting a permissive mask 0777 on file (key_file).\n       Severity: High   Confidence: High\n       CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html)\n       Location: ./examples/os-chmod.py:17\n    16  os.chmod('/etc/passwd', stat.S_IRWXU)\n    17  os.chmod(key_file, 0o777)\n    18\n\n.. seealso::\n\n - https://security.openstack.org/guidelines/dg_apply-restrictive-file-permissions.html\n - https://en.wikipedia.org/wiki/File_system_permissions\n - https://security.openstack.org\n - https://cwe.mitre.org/data/definitions/732.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n.. versionchanged:: 1.7.5\n    Added checks for S_IWGRP and S_IXOTH\n\n\"\"\"  # noqa: E501\nimport stat\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef _stat_is_dangerous(mode):\n    return (\n        mode & stat.S_IWOTH\n        or mode & stat.S_IWGRP\n        or mode & stat.S_IXGRP\n        or mode & stat.S_IXOTH\n    )\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B103\")\ndef set_bad_file_permissions(context):\n    if \"chmod\" in context.call_function_name:\n        if context.call_args_count == 2:\n            mode = context.get_call_arg_at_position(1)\n\n            if (\n                mode is not None\n                and isinstance(mode, int)\n                and _stat_is_dangerous(mode)\n            ):\n                # world writable is an HIGH, group executable is a MEDIUM\n                if mode & stat.S_IWOTH:\n                    sev_level = bandit.HIGH\n                else:\n                    sev_level = bandit.MEDIUM\n\n                filename = context.get_call_arg_at_position(0)\n                if filename is None:\n                    filename = \"NOT PARSED\"\n                return bandit.Issue(\n                    severity=sev_level,\n                    confidence=bandit.HIGH,\n                    cwe=issue.Cwe.INCORRECT_PERMISSION_ASSIGNMENT,\n                    text=\"Chmod setting a permissive mask %s on file (%s).\"\n                    % (oct(mode), filename),\n                )\n"
  },
  {
    "path": "bandit/plugins/general_bind_all_interfaces.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n========================================\nB104: Test for binding to all interfaces\n========================================\n\nBinding to all network interfaces can potentially open up a service to traffic\non unintended interfaces, that may not be properly documented or secured. This\nplugin test looks for a string pattern \"0.0.0.0\" that may indicate a hardcoded\nbinding to all network interfaces.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Possible binding to all interfaces.\n       Severity: Medium   Confidence: Medium\n       CWE: CWE-605 (https://cwe.mitre.org/data/definitions/605.html)\n       Location: ./examples/binding.py:4\n    3   s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n    4   s.bind(('0.0.0.0', 31137))\n    5   s.bind(('192.168.0.1', 8080))\n\n.. seealso::\n\n - https://nvd.nist.gov/vuln/detail/CVE-2018-1281\n - https://cwe.mitre.org/data/definitions/605.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Str\")\n@test.test_id(\"B104\")\ndef hardcoded_bind_all_interfaces(context):\n    if context.string_val == \"0.0.0.0\":  # nosec: B104\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.MEDIUM,\n            cwe=issue.Cwe.MULTIPLE_BINDS,\n            text=\"Possible binding to all interfaces.\",\n        )\n"
  },
  {
    "path": "bandit/plugins/general_hardcoded_password.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\nimport re\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\nRE_WORDS = \"(pas+wo?r?d|pass(phrase)?|pwd|token|secrete?)\"\nRE_CANDIDATES = re.compile(\n    \"(^{0}$|_{0}_|^{0}_|_{0}$)\".format(RE_WORDS), re.IGNORECASE\n)\n\n\ndef _report(value, lineno=None):\n    return bandit.Issue(\n        severity=bandit.LOW,\n        confidence=bandit.MEDIUM,\n        cwe=issue.Cwe.HARD_CODED_PASSWORD,\n        text=f\"Possible hardcoded password: '{value}'\",\n        lineno=lineno,\n    )\n\n\n@test.checks(\"Str\")\n@test.test_id(\"B105\")\ndef hardcoded_password_string(context):\n    \"\"\"**B105: Test for use of hard-coded password strings**\n\n    The use of hard-coded passwords increases the possibility of password\n    guessing tremendously. This plugin test looks for all string literals and\n    checks the following conditions:\n\n    - assigned to a variable that looks like a password\n    - assigned to a dict key that looks like a password\n    - assigned to a class attribute that looks like a password\n    - used in a comparison with a variable that looks like a password\n\n    Variables are considered to look like a password if they have match any one\n    of:\n\n    - \"password\"\n    - \"pass\"\n    - \"passwd\"\n    - \"pwd\"\n    - \"secret\"\n    - \"token\"\n    - \"secrete\"\n\n    Note: this can be noisy and may generate false positives.\n\n    **Config Options:**\n\n    None\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: Possible hardcoded password '(root)'\n           Severity: Low   Confidence: Low\n           CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)\n           Location: ./examples/hardcoded-passwords.py:5\n        4 def someFunction2(password):\n        5     if password == \"root\":\n        6         print(\"OK, logged in\")\n\n    .. seealso::\n\n        - https://www.owasp.org/index.php/Use_of_hard-coded_password\n        - https://cwe.mitre.org/data/definitions/259.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"\n    node = context.node\n    if isinstance(node._bandit_parent, ast.Assign):\n        # looks for \"candidate='some_string'\"\n        for targ in node._bandit_parent.targets:\n            if isinstance(targ, ast.Name) and RE_CANDIDATES.search(targ.id):\n                return _report(node.value)\n            elif isinstance(targ, ast.Attribute) and RE_CANDIDATES.search(\n                targ.attr\n            ):\n                return _report(node.value)\n\n    elif (\n        isinstance(node._bandit_parent, ast.Dict)\n        and node in node._bandit_parent.keys\n        and RE_CANDIDATES.search(node.value)\n    ):\n        # looks for \"{'candidate': 'some_string'}\"\n        dict_node = node._bandit_parent\n        pos = dict_node.keys.index(node)\n        value_node = dict_node.values[pos]\n        if isinstance(value_node, ast.Constant):\n            return _report(value_node.value)\n\n    elif isinstance(\n        node._bandit_parent, ast.Subscript\n    ) and RE_CANDIDATES.search(node.value):\n        # Py39+: looks for \"dict[candidate]='some_string'\"\n        # subscript -> index -> string\n        assign = node._bandit_parent._bandit_parent\n        if (\n            isinstance(assign, ast.Assign)\n            and isinstance(assign.value, ast.Constant)\n            and isinstance(assign.value.value, str)\n        ):\n            return _report(assign.value.value)\n\n    elif isinstance(node._bandit_parent, ast.Index) and RE_CANDIDATES.search(\n        node.value\n    ):\n        # looks for \"dict[candidate]='some_string'\"\n        # assign -> subscript -> index -> string\n        assign = node._bandit_parent._bandit_parent._bandit_parent\n        if (\n            isinstance(assign, ast.Assign)\n            and isinstance(assign.value, ast.Constant)\n            and isinstance(assign.value.value, str)\n        ):\n            return _report(assign.value.value)\n\n    elif isinstance(node._bandit_parent, ast.Compare):\n        # looks for \"candidate == 'some_string'\"\n        comp = node._bandit_parent\n        if isinstance(comp.left, ast.Name):\n            if RE_CANDIDATES.search(comp.left.id):\n                if isinstance(\n                    comp.comparators[0], ast.Constant\n                ) and isinstance(comp.comparators[0].value, str):\n                    return _report(comp.comparators[0].value)\n        elif isinstance(comp.left, ast.Attribute):\n            if RE_CANDIDATES.search(comp.left.attr):\n                if isinstance(\n                    comp.comparators[0], ast.Constant\n                ) and isinstance(comp.comparators[0].value, str):\n                    return _report(comp.comparators[0].value)\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B106\")\ndef hardcoded_password_funcarg(context):\n    \"\"\"**B106: Test for use of hard-coded password function arguments**\n\n    The use of hard-coded passwords increases the possibility of password\n    guessing tremendously. This plugin test looks for all function calls being\n    passed a keyword argument that is a string literal. It checks that the\n    assigned local variable does not look like a password.\n\n    Variables are considered to look like a password if they have match any one\n    of:\n\n    - \"password\"\n    - \"pass\"\n    - \"passwd\"\n    - \"pwd\"\n    - \"secret\"\n    - \"token\"\n    - \"secrete\"\n\n    Note: this can be noisy and may generate false positives.\n\n    **Config Options:**\n\n    None\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B106:hardcoded_password_funcarg] Possible hardcoded\n        password: 'blerg'\n           Severity: Low   Confidence: Medium\n           CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)\n           Location: ./examples/hardcoded-passwords.py:16\n        15\n        16    doLogin(password=\"blerg\")\n\n    .. seealso::\n\n        - https://www.owasp.org/index.php/Use_of_hard-coded_password\n        - https://cwe.mitre.org/data/definitions/259.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"\n    # looks for \"function(candidate='some_string')\"\n    for kw in context.node.keywords:\n        if (\n            isinstance(kw.value, ast.Constant)\n            and isinstance(kw.value.value, str)\n            and RE_CANDIDATES.search(kw.arg)\n        ):\n            return _report(kw.value.value, lineno=kw.value.lineno)\n\n\n@test.checks(\"FunctionDef\")\n@test.test_id(\"B107\")\ndef hardcoded_password_default(context):\n    \"\"\"**B107: Test for use of hard-coded password argument defaults**\n\n    The use of hard-coded passwords increases the possibility of password\n    guessing tremendously. This plugin test looks for all function definitions\n    that specify a default string literal for some argument. It checks that\n    the argument does not look like a password.\n\n    Variables are considered to look like a password if they have match any one\n    of:\n\n    - \"password\"\n    - \"pass\"\n    - \"passwd\"\n    - \"pwd\"\n    - \"secret\"\n    - \"token\"\n    - \"secrete\"\n\n    Note: this can be noisy and may generate false positives.  We do not\n    report on None values which can be legitimately used as a default value,\n    when initializing a function or class.\n\n    **Config Options:**\n\n    None\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B107:hardcoded_password_default] Possible hardcoded\n        password: 'Admin'\n           Severity: Low   Confidence: Medium\n           CWE: CWE-259 (https://cwe.mitre.org/data/definitions/259.html)\n           Location: ./examples/hardcoded-passwords.py:1\n\n        1    def someFunction(user, password=\"Admin\"):\n        2      print(\"Hi \" + user)\n\n    .. seealso::\n\n        - https://www.owasp.org/index.php/Use_of_hard-coded_password\n        - https://cwe.mitre.org/data/definitions/259.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"\n    # looks for \"def function(candidate='some_string')\"\n\n    # this pads the list of default values with \"None\" if nothing is given\n    defs = [None] * (\n        len(context.node.args.args) - len(context.node.args.defaults)\n    )\n    defs.extend(context.node.args.defaults)\n\n    # go through all (param, value)s and look for candidates\n    for key, val in zip(context.node.args.args, defs):\n        if isinstance(key, (ast.Name, ast.arg)):\n            # Skip if the default value is None\n            if val is None or (\n                isinstance(val, ast.Constant) and val.value is None\n            ):\n                continue\n            if (\n                isinstance(val, ast.Constant)\n                and isinstance(val.value, str)\n                and RE_CANDIDATES.search(key.arg)\n            ):\n                return _report(val.value)\n"
  },
  {
    "path": "bandit/plugins/general_hardcoded_tmp.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n===================================================\nB108: Test for insecure usage of tmp file/directory\n===================================================\n\nSafely creating a temporary file or directory means following a number of rules\n(see the references for more details). This plugin test looks for strings\nstarting with (configurable) commonly used temporary paths, for example:\n\n - /tmp\n - /var/tmp\n - /dev/shm\n\n**Config Options:**\n\nThis test plugin takes a similarly named config block,\n`hardcoded_tmp_directory`. The config block provides a Python list, `tmp_dirs`,\nthat lists string fragments indicating possible temporary file paths. Any\nstring starting with one of these fragments will report a MEDIUM confidence\nissue.\n\n.. code-block:: yaml\n\n    hardcoded_tmp_directory:\n        tmp_dirs: ['/tmp', '/var/tmp', '/dev/shm']\n\n\n:Example:\n\n.. code-block: none\n\n    >> Issue: Probable insecure usage of temp file/directory.\n       Severity: Medium   Confidence: Medium\n       CWE: CWE-377 (https://cwe.mitre.org/data/definitions/377.html)\n       Location: ./examples/hardcoded-tmp.py:1\n    1 f = open('/tmp/abc', 'w')\n    2 f.write('def')\n\n.. seealso::\n\n - https://security.openstack.org/guidelines/dg_using-temporary-files-securely.html\n - https://cwe.mitre.org/data/definitions/377.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"  # noqa: E501\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef gen_config(name):\n    if name == \"hardcoded_tmp_directory\":\n        return {\"tmp_dirs\": [\"/tmp\", \"/var/tmp\", \"/dev/shm\"]}  # nosec: B108\n\n\n@test.takes_config\n@test.checks(\"Str\")\n@test.test_id(\"B108\")\ndef hardcoded_tmp_directory(context, config):\n    if config is not None and \"tmp_dirs\" in config:\n        tmp_dirs = config[\"tmp_dirs\"]\n    else:\n        tmp_dirs = [\"/tmp\", \"/var/tmp\", \"/dev/shm\"]  # nosec: B108\n\n    if any(context.string_val.startswith(s) for s in tmp_dirs):\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.MEDIUM,\n            cwe=issue.Cwe.INSECURE_TEMP_FILE,\n            text=\"Probable insecure usage of temp file/directory.\",\n        )\n"
  },
  {
    "path": "bandit/plugins/hashlib_insecure_functions.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n======================================================================\nB324: Test use of insecure md4, md5, or sha1 hash functions in hashlib\n======================================================================\n\nThis plugin checks for the usage of the insecure MD4, MD5, or SHA1 hash\nfunctions in ``hashlib`` and ``crypt``. The ``hashlib.new`` function provides\nthe ability to construct a new hashing object using the named algorithm. This\ncan be used to create insecure hash functions like MD4 and MD5 if they are\npassed as algorithm names to this function.\n\nThis check does additional checking for usage of keyword usedforsecurity on all\nfunction variations of hashlib.\n\nSimilar to ``hashlib``, this plugin also checks for usage of one of the\n``crypt`` module's weak hashes. ``crypt`` also permits MD5 among other weak\nhash variants.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B324:hashlib] Use of weak MD4, MD5, or SHA1 hash for\n       security. Consider usedforsecurity=False\n       Severity: High   Confidence: High\n       CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)\n       Location: examples/hashlib_new_insecure_functions.py:3:0\n       More Info: https://bandit.readthedocs.io/en/latest/plugins/b324_hashlib.html\n    2\n    3   hashlib.new('md5')\n    4\n\n.. seealso::\n\n - https://cwe.mitre.org/data/definitions/327.html\n\n.. versionadded:: 1.5.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n.. versionchanged:: 1.7.6\n    Added check for the crypt module weak hashes\n\n\"\"\"  # noqa: E501\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\nWEAK_HASHES = (\"md4\", \"md5\", \"sha\", \"sha1\")\nWEAK_CRYPT_HASHES = (\"METHOD_CRYPT\", \"METHOD_MD5\", \"METHOD_BLOWFISH\")\n\n\ndef _hashlib_func(context, func):\n    keywords = context.call_keywords\n\n    if func in WEAK_HASHES:\n        if keywords.get(\"usedforsecurity\", \"True\") == \"True\":\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=f\"Use of weak {func.upper()} hash for security. \"\n                \"Consider usedforsecurity=False\",\n                lineno=context.node.lineno,\n            )\n    elif func == \"new\":\n        args = context.call_args\n        name = args[0] if args else keywords.get(\"name\", None)\n        if isinstance(name, str) and name.lower() in WEAK_HASHES:\n            if keywords.get(\"usedforsecurity\", \"True\") == \"True\":\n                return bandit.Issue(\n                    severity=bandit.HIGH,\n                    confidence=bandit.HIGH,\n                    cwe=issue.Cwe.BROKEN_CRYPTO,\n                    text=f\"Use of weak {name.upper()} hash for \"\n                    \"security. Consider usedforsecurity=False\",\n                    lineno=context.node.lineno,\n                )\n\n\ndef _crypt_crypt(context, func):\n    args = context.call_args\n    keywords = context.call_keywords\n\n    if func == \"crypt\":\n        name = args[1] if len(args) > 1 else keywords.get(\"salt\", None)\n        if isinstance(name, str) and name in WEAK_CRYPT_HASHES:\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=f\"Use of insecure crypt.{name.upper()} hash function.\",\n                lineno=context.node.lineno,\n            )\n    elif func == \"mksalt\":\n        name = args[0] if args else keywords.get(\"method\", None)\n        if isinstance(name, str) and name in WEAK_CRYPT_HASHES:\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=f\"Use of insecure crypt.{name.upper()} hash function.\",\n                lineno=context.node.lineno,\n            )\n\n\n@test.test_id(\"B324\")\n@test.checks(\"Call\")\ndef hashlib(context):\n    if isinstance(context.call_function_name_qual, str):\n        qualname_list = context.call_function_name_qual.split(\".\")\n        func = qualname_list[-1]\n\n        if \"hashlib\" in qualname_list:\n            return _hashlib_func(context, func)\n\n        elif \"crypt\" in qualname_list and func in (\"crypt\", \"mksalt\"):\n            return _crypt_crypt(context, func)\n"
  },
  {
    "path": "bandit/plugins/huggingface_unsafe_download.py",
    "content": "# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n================================================\nB615: Test for unsafe Hugging Face Hub downloads\n================================================\n\nThis plugin checks for unsafe downloads from Hugging Face Hub without proper\nintegrity verification. Downloading models, datasets, or files without\nspecifying a revision based on an immmutable revision (commit) can\nlead to supply chain attacks where malicious actors could\nreplace model files and use an existing tag or branch name\nto serve malicious content.\n\nThe secure approach is to:\n\n1. Pin to specific revisions/commits when downloading models, files or datasets\n\nCommon unsafe patterns:\n- ``AutoModel.from_pretrained(\"org/model-name\")``\n- ``AutoModel.from_pretrained(\"org/model-name\", revision=\"main\")``\n- ``AutoModel.from_pretrained(\"org/model-name\", revision=\"v1.0.0\")``\n- ``load_dataset(\"org/dataset-name\")`` without revision\n- ``load_dataset(\"org/dataset-name\", revision=\"main\")``\n- ``load_dataset(\"org/dataset-name\", revision=\"v1.0\")``\n- ``AutoTokenizer.from_pretrained(\"org/model-name\")``\n- ``AutoTokenizer.from_pretrained(\"org/model-name\", revision=\"main\")``\n- ``AutoTokenizer.from_pretrained(\"org/model-name\", revision=\"v3.3.0\")``\n- ``hf_hub_download(repo_id=\"org/model_name\", filename=\"file_name\")``\n- ``hf_hub_download(repo_id=\"org/model_name\",\n        filename=\"file_name\",\n        revision=\"main\"\n        )``\n- ``hf_hub_download(repo_id=\"org/model_name\",\n        filename=\"file_name\",\n        revision=\"v2.0.0\"\n    )``\n- ``snapshot_download(repo_id=\"org/model_name\")``\n- ``snapshot_download(repo_id=\"org/model_name\", revision=\"main\")``\n- ``snapshot_download(repo_id=\"org/model_name\", revision=\"refs/pr/1\")``\n\n\n:Example:\n\n.. code-block:: none\n\n        >> Issue: Unsafe Hugging Face Hub download without revision pinning\n        Severity: Medium   Confidence: High\n        CWE: CWE-494 (https://cwe.mitre.org/data/definitions/494.html)\n        Location: examples/huggingface_unsafe_download.py:8\n        7    # Unsafe: no revision specified\n        8    model = AutoModel.from_pretrained(\"org/model_name\")\n        9\n\n.. seealso::\n\n     - https://cwe.mitre.org/data/definitions/494.html\n     - https://huggingface.co/docs/huggingface_hub/en/guides/download\n\n.. versionadded:: 1.8.6\n\n\"\"\"\nimport ast\nimport string\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B615\")\ndef huggingface_unsafe_download(context):\n    \"\"\"\n    This plugin checks for unsafe artifact download from Hugging Face Hub\n    without immutable/reproducible revision pinning.\n    \"\"\"\n    # Check if any HuggingFace-related modules are imported\n    hf_modules = [\n        \"transformers\",\n        \"datasets\",\n        \"huggingface_hub\",\n    ]\n\n    # Check if any HF modules are imported\n    hf_imported = any(\n        context.is_module_imported_like(module) for module in hf_modules\n    )\n\n    if not hf_imported:\n        return\n\n    qualname = context.call_function_name_qual\n    if not isinstance(qualname, str):\n        return\n\n    unsafe_patterns = {\n        # transformers library patterns\n        \"from_pretrained\": [\"transformers\"],\n        # datasets library patterns\n        \"load_dataset\": [\"datasets\"],\n        # huggingface_hub patterns\n        \"hf_hub_download\": [\"huggingface_hub\"],\n        \"snapshot_download\": [\"huggingface_hub\"],\n        \"repository_id\": [\"huggingface_hub\"],\n    }\n\n    qualname_parts = qualname.split(\".\")\n    func_name = qualname_parts[-1]\n\n    if func_name not in unsafe_patterns:\n        return\n\n    required_modules = unsafe_patterns[func_name]\n    if not any(module in qualname_parts for module in required_modules):\n        return\n\n    # Check for revision parameter (the key security control).\n    # First, check the raw AST to see if a revision/commit_id keyword was\n    # passed as a non-literal expression (variable, attribute, subscript,\n    # function call, etc.).  In those cases we cannot statically determine\n    # the value, so we give the user the benefit of the doubt.\n    call_node = context._context.get(\"call\")\n    if call_node is not None:\n        for kw in getattr(call_node, \"keywords\", []):\n            if kw.arg in (\"revision\", \"commit_id\") and not isinstance(\n                kw.value, ast.Constant\n            ):\n                return\n\n    revision_value = context.get_call_arg_value(\"revision\")\n    commit_id_value = context.get_call_arg_value(\"commit_id\")\n\n    # Check if a revision or commit_id is specified\n    revision_to_check = revision_value or commit_id_value\n\n    if revision_to_check is not None:\n        # Check if it's a secure revision (looks like a commit hash)\n        # Commit hashes: 40 chars (full SHA) or 7+ chars (short SHA)\n        if isinstance(revision_to_check, str):\n            # Remove quotes if present\n            revision_str = str(revision_to_check).strip(\"\\\"'\")\n\n            # Check if it looks like a commit hash (hexadecimal string)\n            # Must be at least 7 characters and all hexadecimal\n            is_hex = all(c in string.hexdigits for c in revision_str)\n            if len(revision_str) >= 7 and is_hex:\n                # This looks like a commit hash, which is secure\n                return\n\n    # Edge case: check if this is a local path (starts with ./ or /)\n    first_arg = context.get_call_arg_at_position(0)\n    if first_arg and isinstance(first_arg, str):\n        if first_arg.startswith((\"./\", \"/\", \"../\")):\n            # Local paths are generally safer\n            return\n\n    return bandit.Issue(\n        severity=bandit.MEDIUM,\n        confidence=bandit.HIGH,\n        text=(\n            f\"Unsafe Hugging Face Hub download without revision pinning \"\n            f\"in {func_name}()\"\n        ),\n        cwe=issue.Cwe.DOWNLOAD_OF_CODE_WITHOUT_INTEGRITY_CHECK,\n        lineno=context.get_lineno_for_call_arg(func_name),\n    )\n"
  },
  {
    "path": "bandit/plugins/injection_paramiko.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==============================================\nB601: Test for shell injection within Paramiko\n==============================================\n\nParamiko is a Python library designed to work with the SSH2 protocol for secure\n(encrypted and authenticated) connections to remote machines. It is intended to\nrun commands on a remote host. These commands are run within a shell on the\ntarget and are thus vulnerable to various shell injection attacks. Bandit\nreports a MEDIUM issue when it detects the use of Paramiko's \"exec_command\"\nmethod advising the user to check inputs are correctly sanitized.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Possible shell injection via Paramiko call, check inputs are\n       properly sanitized.\n       Severity: Medium   Confidence: Medium\n       CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n       Location: ./examples/paramiko_injection.py:4\n    3    # this is not safe\n    4    paramiko.exec_command('something; really; unsafe')\n    5\n\n.. seealso::\n\n - https://security.openstack.org\n - https://github.com/paramiko/paramiko\n - https://www.owasp.org/index.php/Command_Injection\n - https://cwe.mitre.org/data/definitions/78.html\n\n.. versionadded:: 0.12.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B601\")\ndef paramiko_calls(context):\n    issue_text = (\n        \"Possible shell injection via Paramiko call, check inputs \"\n        \"are properly sanitized.\"\n    )\n    for module in [\"paramiko\"]:\n        if context.is_module_imported_like(module):\n            if context.call_function_name in [\"exec_command\"]:\n                return bandit.Issue(\n                    severity=bandit.MEDIUM,\n                    confidence=bandit.MEDIUM,\n                    cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                    text=issue_text,\n                )\n"
  },
  {
    "path": "bandit/plugins/injection_shell.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\nimport re\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n# yuck, regex: starts with a windows drive letter (eg C:)\n# or one of our path delimiter characters (/, \\, .)\nfull_path_match = re.compile(r\"^(?:[A-Za-z](?=\\:)|[\\\\\\/\\.])\")\n\n\ndef _evaluate_shell_call(context):\n    no_formatting = isinstance(\n        context.node.args[0], ast.Constant\n    ) and isinstance(context.node.args[0].value, str)\n\n    if no_formatting:\n        return bandit.LOW\n    else:\n        return bandit.HIGH\n\n\ndef gen_config(name):\n    if name == \"shell_injection\":\n        return {\n            # Start a process using the subprocess module, or one of its\n            # wrappers.\n            \"subprocess\": [\n                \"subprocess.Popen\",\n                \"subprocess.call\",\n                \"subprocess.check_call\",\n                \"subprocess.check_output\",\n                \"subprocess.run\",\n            ],\n            # Start a process with a function vulnerable to shell injection.\n            \"shell\": [\n                \"os.system\",\n                \"os.popen\",\n                \"os.popen2\",\n                \"os.popen3\",\n                \"os.popen4\",\n                \"popen2.popen2\",\n                \"popen2.popen3\",\n                \"popen2.popen4\",\n                \"popen2.Popen3\",\n                \"popen2.Popen4\",\n                \"commands.getoutput\",\n                \"commands.getstatusoutput\",\n                \"subprocess.getoutput\",\n                \"subprocess.getstatusoutput\",\n            ],\n            # Start a process with a function that is not vulnerable to shell\n            # injection.\n            \"no_shell\": [\n                \"os.execl\",\n                \"os.execle\",\n                \"os.execlp\",\n                \"os.execlpe\",\n                \"os.execv\",\n                \"os.execve\",\n                \"os.execvp\",\n                \"os.execvpe\",\n                \"os.spawnl\",\n                \"os.spawnle\",\n                \"os.spawnlp\",\n                \"os.spawnlpe\",\n                \"os.spawnv\",\n                \"os.spawnve\",\n                \"os.spawnvp\",\n                \"os.spawnvpe\",\n                \"os.startfile\",\n            ],\n        }\n\n\ndef has_shell(context):\n    keywords = context.node.keywords\n    result = False\n    if \"shell\" in context.call_keywords:\n        for key in keywords:\n            if key.arg == \"shell\":\n                val = key.value\n                if isinstance(val, ast.Constant) and (\n                    isinstance(val.value, int)\n                    or isinstance(val.value, float)\n                    or isinstance(val.value, complex)\n                ):\n                    result = bool(val.value)\n                elif isinstance(val, ast.List):\n                    result = bool(val.elts)\n                elif isinstance(val, ast.Dict):\n                    result = bool(val.keys)\n                elif isinstance(val, ast.Name) and val.id in [\"False\", \"None\"]:\n                    result = False\n                elif isinstance(val, ast.Constant):\n                    result = val.value\n                else:\n                    result = True\n    return result\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B602\")\ndef subprocess_popen_with_shell_equals_true(context, config):\n    \"\"\"**B602: Test for use of popen with shell equals true**\n\n    Python possesses many mechanisms to invoke an external executable. However,\n    doing so may present a security issue if appropriate care is not taken to\n    sanitize any user provided or variable input.\n\n    This plugin test is part of a family of tests built to check for process\n    spawning and warn appropriately. Specifically, this test looks for the\n    spawning of a subprocess using a command shell. This type of subprocess\n    invocation is dangerous as it is vulnerable to various shell injection\n    attacks. Great care should be taken to sanitize all input in order to\n    mitigate this risk. Calls of this type are identified by a parameter of\n    'shell=True' being given.\n\n    Additionally, this plugin scans the command string given and adjusts its\n    reported severity based on how it is presented. If the command string is a\n    simple static string containing no special shell characters, then the\n    resulting issue has low severity. If the string is static, but contains\n    shell formatting characters or wildcards, then the reported issue is\n    medium. Finally, if the string is computed using Python's string\n    manipulation or formatting operations, then the reported issue has high\n    severity. These severity levels reflect the likelihood that the code is\n    vulnerable to injection.\n\n    See also:\n\n    - :doc:`../plugins/linux_commands_wildcard_injection`\n    - :doc:`../plugins/subprocess_without_shell_equals_true`\n    - :doc:`../plugins/start_process_with_no_shell`\n    - :doc:`../plugins/start_process_with_a_shell`\n    - :doc:`../plugins/start_process_with_partial_path`\n\n    **Config Options:**\n\n    This plugin test shares a configuration with others in the same family,\n    namely `shell_injection`. This configuration is divided up into three\n    sections, `subprocess`, `shell` and `no_shell`. They each list Python calls\n    that spawn subprocesses, invoke commands within a shell, or invoke commands\n    without a shell (by replacing the calling process) respectively.\n\n    This plugin specifically scans for methods listed in `subprocess` section\n    that have shell=True specified.\n\n    .. code-block:: yaml\n\n        shell_injection:\n\n            # Start a process using the subprocess module, or one of its\n            wrappers.\n            subprocess:\n                - subprocess.Popen\n                - subprocess.call\n\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: subprocess call with shell=True seems safe, but may be\n        changed in the future, consider rewriting without shell\n           Severity: Low   Confidence: High\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: ./examples/subprocess_shell.py:21\n        20  subprocess.check_call(['/bin/ls', '-l'], shell=False)\n        21  subprocess.check_call('/bin/ls -l', shell=True)\n        22\n\n        >> Issue: call with shell=True contains special shell characters,\n        consider moving extra logic into Python code\n           Severity: Medium   Confidence: High\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: ./examples/subprocess_shell.py:26\n        25\n        26  subprocess.Popen('/bin/ls *', shell=True)\n        27  subprocess.Popen('/bin/ls %s' % ('something',), shell=True)\n\n        >> Issue: subprocess call with shell=True identified, security issue.\n           Severity: High   Confidence: High\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: ./examples/subprocess_shell.py:27\n        26  subprocess.Popen('/bin/ls *', shell=True)\n        27  subprocess.Popen('/bin/ls %s' % ('something',), shell=True)\n        28  subprocess.Popen('/bin/ls {}'.format('something'), shell=True)\n\n    .. seealso::\n\n     - https://security.openstack.org\n     - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments\n     - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html\n     - https://security.openstack.org/guidelines/dg_avoid-shell-true.html\n     - https://cwe.mitre.org/data/definitions/78.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    if config and context.call_function_name_qual in config[\"subprocess\"]:\n        if has_shell(context):\n            if len(context.call_args) > 0:\n                sev = _evaluate_shell_call(context)\n                if sev == bandit.LOW:\n                    return bandit.Issue(\n                        severity=bandit.LOW,\n                        confidence=bandit.HIGH,\n                        cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                        text=\"subprocess call with shell=True seems safe, but \"\n                        \"may be changed in the future, consider \"\n                        \"rewriting without shell\",\n                        lineno=context.get_lineno_for_call_arg(\"shell\"),\n                    )\n                else:\n                    return bandit.Issue(\n                        severity=bandit.HIGH,\n                        confidence=bandit.HIGH,\n                        cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                        text=\"subprocess call with shell=True identified, \"\n                        \"security issue.\",\n                        lineno=context.get_lineno_for_call_arg(\"shell\"),\n                    )\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B603\")\ndef subprocess_without_shell_equals_true(context, config):\n    \"\"\"**B603: Test for use of subprocess without shell equals true**\n\n    Python possesses many mechanisms to invoke an external executable. However,\n    doing so may present a security issue if appropriate care is not taken to\n    sanitize any user provided or variable input.\n\n    This plugin test is part of a family of tests built to check for process\n    spawning and warn appropriately. Specifically, this test looks for the\n    spawning of a subprocess without the use of a command shell. This type of\n    subprocess invocation is not vulnerable to shell injection attacks, but\n    care should still be taken to ensure validity of input.\n\n    Because this is a lesser issue than that described in\n    `subprocess_popen_with_shell_equals_true` a LOW severity warning is\n    reported.\n\n    See also:\n\n    - :doc:`../plugins/linux_commands_wildcard_injection`\n    - :doc:`../plugins/subprocess_popen_with_shell_equals_true`\n    - :doc:`../plugins/start_process_with_no_shell`\n    - :doc:`../plugins/start_process_with_a_shell`\n    - :doc:`../plugins/start_process_with_partial_path`\n\n    **Config Options:**\n\n    This plugin test shares a configuration with others in the same family,\n    namely `shell_injection`. This configuration is divided up into three\n    sections, `subprocess`, `shell` and `no_shell`. They each list Python calls\n    that spawn subprocesses, invoke commands within a shell, or invoke commands\n    without a shell (by replacing the calling process) respectively.\n\n    This plugin specifically scans for methods listed in `subprocess` section\n    that have shell=False specified.\n\n    .. code-block:: yaml\n\n        shell_injection:\n            # Start a process using the subprocess module, or one of its\n            wrappers.\n            subprocess:\n                - subprocess.Popen\n                - subprocess.call\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: subprocess call - check for execution of untrusted input.\n           Severity: Low   Confidence: High\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: ./examples/subprocess_shell.py:23\n        22\n        23    subprocess.check_output(['/bin/ls', '-l'])\n        24\n\n    .. seealso::\n\n     - https://security.openstack.org\n     - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments\n     - https://security.openstack.org/guidelines/dg_avoid-shell-true.html\n     - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html\n     - https://cwe.mitre.org/data/definitions/78.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    if config and context.call_function_name_qual in config[\"subprocess\"]:\n        if not has_shell(context):\n            return bandit.Issue(\n                severity=bandit.LOW,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                text=\"subprocess call - check for execution of untrusted \"\n                \"input.\",\n                lineno=context.get_lineno_for_call_arg(\"shell\"),\n            )\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B604\")\ndef any_other_function_with_shell_equals_true(context, config):\n    \"\"\"**B604: Test for any function with shell equals true**\n\n    Python possesses many mechanisms to invoke an external executable. However,\n    doing so may present a security issue if appropriate care is not taken to\n    sanitize any user provided or variable input.\n\n    This plugin test is part of a family of tests built to check for process\n    spawning and warn appropriately. Specifically, this plugin test\n    interrogates method calls for the presence of a keyword parameter `shell`\n    equalling true. It is related to detection of shell injection issues and is\n    intended to catch custom wrappers to vulnerable methods that may have been\n    created.\n\n    See also:\n\n    - :doc:`../plugins/linux_commands_wildcard_injection`\n    - :doc:`../plugins/subprocess_popen_with_shell_equals_true`\n    - :doc:`../plugins/subprocess_without_shell_equals_true`\n    - :doc:`../plugins/start_process_with_no_shell`\n    - :doc:`../plugins/start_process_with_a_shell`\n    - :doc:`../plugins/start_process_with_partial_path`\n\n    **Config Options:**\n\n    This plugin test shares a configuration with others in the same family,\n    namely `shell_injection`. This configuration is divided up into three\n    sections, `subprocess`, `shell` and `no_shell`. They each list Python calls\n    that spawn subprocesses, invoke commands within a shell, or invoke commands\n    without a shell (by replacing the calling process) respectively.\n\n    Specifically, this plugin excludes those functions listed under the\n    subprocess section, these methods are tested in a separate specific test\n    plugin and this exclusion prevents duplicate issue reporting.\n\n    .. code-block:: yaml\n\n        shell_injection:\n            # Start a process using the subprocess module, or one of its\n            wrappers.\n            subprocess: [subprocess.Popen, subprocess.call,\n                         subprocess.check_call, subprocess.check_output\n                         execute_with_timeout]\n\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: Function call with shell=True parameter identified, possible\n        security issue.\n           Severity: Medium   Confidence: High\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: ./examples/subprocess_shell.py:9\n        8 pop('/bin/gcc --version', shell=True)\n        9 Popen('/bin/gcc --version', shell=True)\n        10\n\n    .. seealso::\n\n     - https://security.openstack.org/guidelines/dg_avoid-shell-true.html\n     - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html\n     - https://cwe.mitre.org/data/definitions/78.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    if config and context.call_function_name_qual not in config[\"subprocess\"]:\n        if has_shell(context):\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.LOW,\n                cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                text=\"Function call with shell=True parameter identified, \"\n                \"possible security issue.\",\n                lineno=context.get_lineno_for_call_arg(\"shell\"),\n            )\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B605\")\ndef start_process_with_a_shell(context, config):\n    \"\"\"**B605: Test for starting a process with a shell**\n\n    Python possesses many mechanisms to invoke an external executable. However,\n    doing so may present a security issue if appropriate care is not taken to\n    sanitize any user provided or variable input.\n\n    This plugin test is part of a family of tests built to check for process\n    spawning and warn appropriately. Specifically, this test looks for the\n    spawning of a subprocess using a command shell. This type of subprocess\n    invocation is dangerous as it is vulnerable to various shell injection\n    attacks. Great care should be taken to sanitize all input in order to\n    mitigate this risk. Calls of this type are identified by the use of certain\n    commands which are known to use shells. Bandit will report a LOW\n    severity warning.\n\n    See also:\n\n    - :doc:`../plugins/linux_commands_wildcard_injection`\n    - :doc:`../plugins/subprocess_without_shell_equals_true`\n    - :doc:`../plugins/start_process_with_no_shell`\n    - :doc:`../plugins/start_process_with_partial_path`\n    - :doc:`../plugins/subprocess_popen_with_shell_equals_true`\n\n    **Config Options:**\n\n    This plugin test shares a configuration with others in the same family,\n    namely `shell_injection`. This configuration is divided up into three\n    sections, `subprocess`, `shell` and `no_shell`. They each list Python calls\n    that spawn subprocesses, invoke commands within a shell, or invoke commands\n    without a shell (by replacing the calling process) respectively.\n\n    This plugin specifically scans for methods listed in `shell` section.\n\n    .. code-block:: yaml\n\n        shell_injection:\n            shell:\n                - os.system\n                - os.popen\n                - os.popen2\n                - os.popen3\n                - os.popen4\n                - popen2.popen2\n                - popen2.popen3\n                - popen2.popen4\n                - popen2.Popen3\n                - popen2.Popen4\n                - commands.getoutput\n                - commands.getstatusoutput\n                - subprocess.getoutput\n                - subprocess.getstatusoutput\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: Starting a process with a shell: check for injection.\n           Severity: Low   Confidence: Medium\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: examples/os_system.py:3\n        2\n        3   os.system('/bin/echo hi')\n\n    .. seealso::\n\n     - https://security.openstack.org\n     - https://docs.python.org/3/library/os.html#os.system\n     - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments\n     - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html\n     - https://cwe.mitre.org/data/definitions/78.html\n\n    .. versionadded:: 0.10.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n    if config and context.call_function_name_qual in config[\"shell\"]:\n        if len(context.call_args) > 0:\n            sev = _evaluate_shell_call(context)\n            if sev == bandit.LOW:\n                return bandit.Issue(\n                    severity=bandit.LOW,\n                    confidence=bandit.HIGH,\n                    cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                    text=\"Starting a process with a shell: \"\n                    \"Seems safe, but may be changed in the future, \"\n                    \"consider rewriting without shell\",\n                )\n            else:\n                return bandit.Issue(\n                    severity=bandit.HIGH,\n                    confidence=bandit.HIGH,\n                    cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                    text=\"Starting a process with a shell, possible injection\"\n                    \" detected, security issue.\",\n                )\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B606\")\ndef start_process_with_no_shell(context, config):\n    \"\"\"**B606: Test for starting a process with no shell**\n\n    Python possesses many mechanisms to invoke an external executable. However,\n    doing so may present a security issue if appropriate care is not taken to\n    sanitize any user provided or variable input.\n\n    This plugin test is part of a family of tests built to check for process\n    spawning and warn appropriately. Specifically, this test looks for the\n    spawning of a subprocess in a way that doesn't use a shell. Although this\n    is generally safe, it maybe useful for penetration testing workflows to\n    track where external system calls are used.  As such a LOW severity message\n    is generated.\n\n    See also:\n\n    - :doc:`../plugins/linux_commands_wildcard_injection`\n    - :doc:`../plugins/subprocess_without_shell_equals_true`\n    - :doc:`../plugins/start_process_with_a_shell`\n    - :doc:`../plugins/start_process_with_partial_path`\n    - :doc:`../plugins/subprocess_popen_with_shell_equals_true`\n\n    **Config Options:**\n\n    This plugin test shares a configuration with others in the same family,\n    namely `shell_injection`. This configuration is divided up into three\n    sections, `subprocess`, `shell` and `no_shell`. They each list Python calls\n    that spawn subprocesses, invoke commands within a shell, or invoke commands\n    without a shell (by replacing the calling process) respectively.\n\n    This plugin specifically scans for methods listed in `no_shell` section.\n\n    .. code-block:: yaml\n\n        shell_injection:\n            no_shell:\n                - os.execl\n                - os.execle\n                - os.execlp\n                - os.execlpe\n                - os.execv\n                - os.execve\n                - os.execvp\n                - os.execvpe\n                - os.spawnl\n                - os.spawnle\n                - os.spawnlp\n                - os.spawnlpe\n                - os.spawnv\n                - os.spawnve\n                - os.spawnvp\n                - os.spawnvpe\n                - os.startfile\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [start_process_with_no_shell] Starting a process without a\n           shell.\n           Severity: Low   Confidence: Medium\n           CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n           Location: examples/os-spawn.py:8\n        7   os.spawnv(mode, path, args)\n        8   os.spawnve(mode, path, args, env)\n        9   os.spawnvp(mode, file, args)\n\n    .. seealso::\n\n     - https://security.openstack.org\n     - https://docs.python.org/3/library/os.html#os.system\n     - https://docs.python.org/3/library/subprocess.html#frequently-used-arguments\n     - https://security.openstack.org/guidelines/dg_use-subprocess-securely.html\n     - https://cwe.mitre.org/data/definitions/78.html\n\n    .. versionadded:: 0.10.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n\n    if config and context.call_function_name_qual in config[\"no_shell\"]:\n        return bandit.Issue(\n            severity=bandit.LOW,\n            confidence=bandit.MEDIUM,\n            cwe=issue.Cwe.OS_COMMAND_INJECTION,\n            text=\"Starting a process without a shell.\",\n        )\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B607\")\ndef start_process_with_partial_path(context, config):\n    \"\"\"**B607: Test for starting a process with a partial path**\n\n    Python possesses many mechanisms to invoke an external executable. If the\n    desired executable path is not fully qualified relative to the filesystem\n    root then this may present a potential security risk.\n\n    In POSIX environments, the `PATH` environment variable is used to specify a\n    set of standard locations that will be searched for the first matching\n    named executable. While convenient, this behavior may allow a malicious\n    actor to exert control over a system. If they are able to adjust the\n    contents of the `PATH` variable, or manipulate the file system, then a\n    bogus executable may be discovered in place of the desired one. This\n    executable will be invoked with the user privileges of the Python process\n    that spawned it, potentially a highly privileged user.\n\n    This test will scan the parameters of all configured Python methods,\n    looking for paths that do not start at the filesystem root, that is, do not\n    have a leading '/' character.\n\n    **Config Options:**\n\n    This plugin test shares a configuration with others in the same family,\n    namely `shell_injection`. This configuration is divided up into three\n    sections, `subprocess`, `shell` and `no_shell`. They each list Python calls\n    that spawn subprocesses, invoke commands within a shell, or invoke commands\n    without a shell (by replacing the calling process) respectively.\n\n    This test will scan parameters of all methods in all sections. Note that\n    methods are fully qualified and de-aliased prior to checking.\n\n    .. code-block:: yaml\n\n        shell_injection:\n            # Start a process using the subprocess module, or one of its\n            wrappers.\n            subprocess:\n                - subprocess.Popen\n                - subprocess.call\n\n            # Start a process with a function vulnerable to shell injection.\n            shell:\n                - os.system\n                - os.popen\n                - popen2.Popen3\n                - popen2.Popen4\n                - commands.getoutput\n                - commands.getstatusoutput\n            # Start a process with a function that is not vulnerable to shell\n            injection.\n            no_shell:\n                - os.execl\n                - os.execle\n\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: Starting a process with a partial executable path\n        Severity: Low   Confidence: High\n        CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n        Location: ./examples/partial_path_process.py:3\n        2    from subprocess import Popen as pop\n        3    pop('gcc --version', shell=False)\n\n    .. seealso::\n\n     - https://security.openstack.org\n     - https://docs.python.org/3/library/os.html#process-management\n     - https://cwe.mitre.org/data/definitions/78.html\n\n    .. versionadded:: 0.13.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"\n\n    if config and len(context.call_args):\n        if (\n            context.call_function_name_qual in config[\"subprocess\"]\n            or context.call_function_name_qual in config[\"shell\"]\n            or context.call_function_name_qual in config[\"no_shell\"]\n        ):\n            node = context.node.args[0]\n            # some calls take an arg list, check the first part\n            if isinstance(node, ast.List) and node.elts:\n                node = node.elts[0]\n\n            # make sure the param is a string literal and not a var name\n            if (\n                isinstance(node, ast.Constant)\n                and isinstance(node.value, str)\n                and not full_path_match.match(node.value)\n            ):\n                return bandit.Issue(\n                    severity=bandit.LOW,\n                    confidence=bandit.HIGH,\n                    cwe=issue.Cwe.OS_COMMAND_INJECTION,\n                    text=\"Starting a process with a partial executable path\",\n                )\n"
  },
  {
    "path": "bandit/plugins/injection_sql.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n============================\nB608: Test for SQL injection\n============================\n\nAn SQL injection attack consists of insertion or \"injection\" of a SQL query via\nthe input data given to an application. It is a very common attack vector. This\nplugin test looks for strings that resemble SQL statements that are involved in\nsome form of string building operation. For example:\n\n - \"SELECT %s FROM derp;\" % var\n - \"SELECT thing FROM \" + tab\n - \"SELECT \" + val + \" FROM \" + tab + ...\n - \"SELECT {} FROM derp;\".format(var)\n - f\"SELECT foo FROM bar WHERE id = {product}\"\n\nUnless care is taken to sanitize and control the input data when building such\nSQL statement strings, an injection attack becomes possible. If strings of this\nnature are discovered, a LOW confidence issue is reported. In order to boost\nresult confidence, this plugin test will also check to see if the discovered\nstring is in use with standard Python DBAPI calls `execute` or `executemany`.\nIf so, a MEDIUM issue is reported. For example:\n\n - cursor.execute(\"SELECT %s FROM derp;\" % var)\n\nUse of str.replace in the string construction can also be dangerous.\nFor example:\n\n- \"SELECT * FROM foo WHERE id = '[VALUE]'\".replace(\"[VALUE]\", identifier)\n\nHowever, such cases are always reported with LOW confidence to compensate\nfor false positives, since valid uses of str.replace can be common.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Possible SQL injection vector through string-based query\n    construction.\n       Severity: Medium   Confidence: Low\n       CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)\n       Location: ./examples/sql_statements.py:4\n    3 query = \"DELETE FROM foo WHERE id = '%s'\" % identifier\n    4 query = \"UPDATE foo SET value = 'b' WHERE id = '%s'\" % identifier\n    5\n\n.. seealso::\n\n - https://www.owasp.org/index.php/SQL_Injection\n - https://security.openstack.org/guidelines/dg_parameterize-database-queries.html\n - https://cwe.mitre.org/data/definitions/89.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n.. versionchanged:: 1.7.7\n    Flag when str.replace is used in the string construction\n\n\"\"\"  # noqa: E501\nimport ast\nimport re\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\nfrom bandit.core import utils\n\nSIMPLE_SQL_RE = re.compile(\n    r\"(select\\s.*from\\s|\"\n    r\"delete\\s+from\\s|\"\n    r\"insert\\s+into\\s.*values[\\s(]|\"\n    r\"update\\s.*set\\s)\",\n    re.IGNORECASE | re.DOTALL,\n)\n\n\ndef _check_string(data):\n    return SIMPLE_SQL_RE.search(data) is not None\n\n\ndef _evaluate_ast(node):\n    wrapper = None\n    statement = \"\"\n    str_replace = False\n\n    if isinstance(node._bandit_parent, ast.BinOp):\n        out = utils.concat_string(node, node._bandit_parent)\n        wrapper = out[0]._bandit_parent\n        statement = out[1]\n    elif isinstance(\n        node._bandit_parent, ast.Attribute\n    ) and node._bandit_parent.attr in (\"format\", \"replace\"):\n        statement = node.value\n        # Hierarchy for \"\".format() is Wrapper -> Call -> Attribute -> Str\n        wrapper = node._bandit_parent._bandit_parent._bandit_parent\n        if node._bandit_parent.attr == \"replace\":\n            str_replace = True\n    elif hasattr(ast, \"JoinedStr\") and isinstance(\n        node._bandit_parent, ast.JoinedStr\n    ):\n        substrings = [\n            child\n            for child in node._bandit_parent.values\n            if isinstance(child, ast.Constant) and isinstance(child.value, str)\n        ]\n        # JoinedStr consists of list of Constant and FormattedValue\n        # instances. Let's perform one test for the whole string\n        # and abandon all parts except the first one to raise one\n        # failed test instead of many for the same SQL statement.\n        if substrings and node == substrings[0]:\n            statement = \"\".join([str(child.value) for child in substrings])\n            wrapper = node._bandit_parent._bandit_parent\n\n    if isinstance(wrapper, ast.Call):  # wrapped in \"execute\" call?\n        names = [\"execute\", \"executemany\"]\n        name = utils.get_called_name(wrapper)\n        return (name in names, statement, str_replace)\n    else:\n        return (False, statement, str_replace)\n\n\n@test.checks(\"Str\")\n@test.test_id(\"B608\")\ndef hardcoded_sql_expressions(context):\n    execute_call, statement, str_replace = _evaluate_ast(context.node)\n    if _check_string(statement):\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=(\n                bandit.MEDIUM\n                if execute_call and not str_replace\n                else bandit.LOW\n            ),\n            cwe=issue.Cwe.SQL_INJECTION,\n            text=\"Possible SQL injection vector through string-based \"\n            \"query construction.\",\n        )\n"
  },
  {
    "path": "bandit/plugins/injection_wildcard.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n========================================\nB609: Test for use of wildcard injection\n========================================\n\nPython provides a number of methods that emulate the behavior of standard Linux\ncommand line utilities. Like their Linux counterparts, these commands may take\na wildcard \"\\*\" character in place of a file system path. This is interpreted\nto mean \"any and all files or folders\" and can be used to build partially\nqualified paths, such as \"/home/user/\\*\".\n\nThe use of partially qualified paths may result in unintended consequences if\nan unexpected file or symlink is placed into the path location given. This\nbecomes particularly dangerous when combined with commands used to manipulate\nfile permissions or copy data off of a system.\n\nThis test plugin looks for usage of the following commands in conjunction with\nwild card parameters:\n\n- 'chown'\n- 'chmod'\n- 'tar'\n- 'rsync'\n\nAs well as any method configured in the shell or subprocess injection test\nconfigurations.\n\n\n**Config Options:**\n\nThis plugin test shares a configuration with others in the same family, namely\n`shell_injection`. This configuration is divided up into three sections,\n`subprocess`, `shell` and `no_shell`. They each list Python calls that spawn\nsubprocesses, invoke commands within a shell, or invoke commands without a\nshell (by replacing the calling process) respectively.\n\nThis test will scan parameters of all methods in all sections. Note that\nmethods are fully qualified and de-aliased prior to checking.\n\n\n.. code-block:: yaml\n\n    shell_injection:\n        # Start a process using the subprocess module, or one of its wrappers.\n        subprocess:\n            - subprocess.Popen\n            - subprocess.call\n\n        # Start a process with a function vulnerable to shell injection.\n        shell:\n            - os.system\n            - os.popen\n            - popen2.Popen3\n            - popen2.Popen4\n            - commands.getoutput\n            - commands.getstatusoutput\n        # Start a process with a function that is not vulnerable to shell\n        injection.\n        no_shell:\n            - os.execl\n            - os.execle\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Possible wildcard injection in call: subprocess.Popen\n       Severity: High   Confidence: Medium\n       CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n       Location: ./examples/wildcard-injection.py:8\n    7    o.popen2('/bin/chmod *')\n    8    subp.Popen('/bin/chown *', shell=True)\n    9\n\n    >> Issue: subprocess call - check for execution of untrusted input.\n       Severity: Low   Confidence: High\n       CWE-78 (https://cwe.mitre.org/data/definitions/78.html)\n       Location: ./examples/wildcard-injection.py:11\n    10   # Not vulnerable to wildcard injection\n    11   subp.Popen('/bin/rsync *')\n    12   subp.Popen(\"/bin/chmod *\")\n\n\n.. seealso::\n\n - https://security.openstack.org\n - https://en.wikipedia.org/wiki/Wildcard_character\n - https://www.defensecode.com/public/DefenseCode_Unix_WildCards_Gone_Wild.txt\n - https://cwe.mitre.org/data/definitions/78.html\n\n.. versionadded:: 0.9.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\nfrom bandit.plugins import injection_shell  # NOTE(tkelsey): shared config\n\ngen_config = injection_shell.gen_config\n\n\n@test.takes_config(\"shell_injection\")\n@test.checks(\"Call\")\n@test.test_id(\"B609\")\ndef linux_commands_wildcard_injection(context, config):\n    if not (\"shell\" in config and \"subprocess\" in config):\n        return\n\n    vulnerable_funcs = [\"chown\", \"chmod\", \"tar\", \"rsync\"]\n    if context.call_function_name_qual in config[\"shell\"] or (\n        context.call_function_name_qual in config[\"subprocess\"]\n        and context.check_call_arg_value(\"shell\", \"True\")\n    ):\n        if context.call_args_count >= 1:\n            call_argument = context.get_call_arg_at_position(0)\n            argument_string = \"\"\n            if isinstance(call_argument, list):\n                for li in call_argument:\n                    argument_string += f\" {li}\"\n            elif isinstance(call_argument, str):\n                argument_string = call_argument\n\n            if argument_string != \"\":\n                for vulnerable_func in vulnerable_funcs:\n                    if (\n                        vulnerable_func in argument_string\n                        and \"*\" in argument_string\n                    ):\n                        return bandit.Issue(\n                            severity=bandit.HIGH,\n                            confidence=bandit.MEDIUM,\n                            cwe=issue.Cwe.IMPROPER_WILDCARD_NEUTRALIZATION,\n                            text=\"Possible wildcard injection in call: %s\"\n                            % context.call_function_name_qual,\n                            lineno=context.get_lineno_for_call_arg(\"shell\"),\n                        )\n"
  },
  {
    "path": "bandit/plugins/insecure_ssl_tls.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef get_bad_proto_versions(config):\n    return config[\"bad_protocol_versions\"]\n\n\ndef gen_config(name):\n    if name == \"ssl_with_bad_version\":\n        return {\n            \"bad_protocol_versions\": [\n                \"PROTOCOL_SSLv2\",\n                \"SSLv2_METHOD\",\n                \"SSLv23_METHOD\",\n                \"PROTOCOL_SSLv3\",  # strict option\n                \"PROTOCOL_TLSv1\",  # strict option\n                \"SSLv3_METHOD\",  # strict option\n                \"TLSv1_METHOD\",\n                \"PROTOCOL_TLSv1_1\",\n                \"TLSv1_1_METHOD\",\n            ]\n        }  # strict option\n\n\n@test.takes_config\n@test.checks(\"Call\")\n@test.test_id(\"B502\")\ndef ssl_with_bad_version(context, config):\n    \"\"\"**B502: Test for SSL use with bad version used**\n\n    Several highly publicized exploitable flaws have been discovered\n    in all versions of SSL and early versions of TLS. It is strongly\n    recommended that use of the following known broken protocol versions be\n    avoided:\n\n    - SSL v2\n    - SSL v3\n    - TLS v1\n    - TLS v1.1\n\n    This plugin test scans for calls to Python methods with parameters that\n    indicate the used broken SSL/TLS protocol versions. Currently, detection\n    supports methods using Python's native SSL/TLS support and the pyOpenSSL\n    module. A HIGH severity warning will be reported whenever known broken\n    protocol versions are detected.\n\n    It is worth noting that native support for TLS 1.2 is only available in\n    more recent Python versions, specifically 2.7.9 and up, and 3.x\n\n    A note on 'SSLv23':\n\n    Amongst the available SSL/TLS versions provided by Python/pyOpenSSL there\n    exists the option to use SSLv23. This very poorly named option actually\n    means \"use the highest version of SSL/TLS supported by both the server and\n    client\". This may (and should be) a version well in advance of SSL v2 or\n    v3. Bandit can scan for the use of SSLv23 if desired, but its detection\n    does not necessarily indicate a problem.\n\n    When using SSLv23 it is important to also provide flags to explicitly\n    exclude bad versions of SSL/TLS from the protocol versions considered. Both\n    the Python native and pyOpenSSL modules provide the ``OP_NO_SSLv2`` and\n    ``OP_NO_SSLv3`` flags for this purpose.\n\n    **Config Options:**\n\n    .. code-block:: yaml\n\n        ssl_with_bad_version:\n            bad_protocol_versions:\n                - PROTOCOL_SSLv2\n                - SSLv2_METHOD\n                - SSLv23_METHOD\n                - PROTOCOL_SSLv3  # strict option\n                - PROTOCOL_TLSv1  # strict option\n                - SSLv3_METHOD    # strict option\n                - TLSv1_METHOD    # strict option\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: ssl.wrap_socket call with insecure SSL/TLS protocol version\n        identified, security issue.\n           Severity: High   Confidence: High\n           CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)\n           Location: ./examples/ssl-insecure-version.py:13\n        12  # strict tests\n        13  ssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3)\n        14  ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1)\n\n    .. seealso::\n\n     - :func:`ssl_with_bad_defaults`\n     - :func:`ssl_with_no_version`\n     - https://heartbleed.com/\n     - https://en.wikipedia.org/wiki/POODLE\n     - https://security.openstack.org/guidelines/dg_move-data-securely.html\n     - https://cwe.mitre.org/data/definitions/327.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    .. versionchanged:: 1.7.5\n        Added TLS 1.1\n\n    \"\"\"\n    bad_ssl_versions = get_bad_proto_versions(config)\n    if context.call_function_name_qual == \"ssl.wrap_socket\":\n        if context.check_call_arg_value(\"ssl_version\", bad_ssl_versions):\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=\"ssl.wrap_socket call with insecure SSL/TLS protocol \"\n                \"version identified, security issue.\",\n                lineno=context.get_lineno_for_call_arg(\"ssl_version\"),\n            )\n    elif context.call_function_name_qual == \"pyOpenSSL.SSL.Context\":\n        if context.check_call_arg_value(\"method\", bad_ssl_versions):\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=\"SSL.Context call with insecure SSL/TLS protocol \"\n                \"version identified, security issue.\",\n                lineno=context.get_lineno_for_call_arg(\"method\"),\n            )\n\n    elif (\n        context.call_function_name_qual != \"ssl.wrap_socket\"\n        and context.call_function_name_qual != \"pyOpenSSL.SSL.Context\"\n    ):\n        if context.check_call_arg_value(\n            \"method\", bad_ssl_versions\n        ) or context.check_call_arg_value(\"ssl_version\", bad_ssl_versions):\n            lineno = context.get_lineno_for_call_arg(\n                \"method\"\n            ) or context.get_lineno_for_call_arg(\"ssl_version\")\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.MEDIUM,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=\"Function call with insecure SSL/TLS protocol \"\n                \"identified, possible security issue.\",\n                lineno=lineno,\n            )\n\n\n@test.takes_config(\"ssl_with_bad_version\")\n@test.checks(\"FunctionDef\")\n@test.test_id(\"B503\")\ndef ssl_with_bad_defaults(context, config):\n    \"\"\"**B503: Test for SSL use with bad defaults specified**\n\n    This plugin is part of a family of tests that detect the use of known bad\n    versions of SSL/TLS, please see :doc:`../plugins/ssl_with_bad_version` for\n    a complete discussion. Specifically, this plugin test scans for Python\n    methods with default parameter values that specify the use of broken\n    SSL/TLS protocol versions. Currently, detection supports methods using\n    Python's native SSL/TLS support and the pyOpenSSL module. A MEDIUM severity\n    warning will be reported whenever known broken protocol versions are\n    detected.\n\n    **Config Options:**\n\n    This test shares the configuration provided for the standard\n    :doc:`../plugins/ssl_with_bad_version` test, please refer to its\n    documentation.\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: Function definition identified with insecure SSL/TLS protocol\n        version by default, possible security issue.\n           Severity: Medium   Confidence: Medium\n           CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)\n           Location: ./examples/ssl-insecure-version.py:28\n        27\n        28  def open_ssl_socket(version=SSL.SSLv2_METHOD):\n        29      pass\n\n    .. seealso::\n\n     - :func:`ssl_with_bad_version`\n     - :func:`ssl_with_no_version`\n     - https://heartbleed.com/\n     - https://en.wikipedia.org/wiki/POODLE\n     - https://security.openstack.org/guidelines/dg_move-data-securely.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    .. versionchanged:: 1.7.5\n        Added TLS 1.1\n\n    \"\"\"\n\n    bad_ssl_versions = get_bad_proto_versions(config)\n    for default in context.function_def_defaults_qual:\n        val = default.split(\".\")[-1]\n        if val in bad_ssl_versions:\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.MEDIUM,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=\"Function definition identified with insecure SSL/TLS \"\n                \"protocol version by default, possible security \"\n                \"issue.\",\n            )\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B504\")\ndef ssl_with_no_version(context):\n    \"\"\"**B504: Test for SSL use with no version specified**\n\n    This plugin is part of a family of tests that detect the use of known bad\n    versions of SSL/TLS, please see :doc:`../plugins/ssl_with_bad_version` for\n    a complete discussion. Specifically, This plugin test scans for specific\n    methods in Python's native SSL/TLS support and the pyOpenSSL module that\n    configure the version of SSL/TLS protocol to use. These methods are known\n    to provide default value that maximize compatibility, but permit use of the\n    aforementioned broken protocol versions. A LOW severity warning will be\n    reported whenever this is detected.\n\n    **Config Options:**\n\n    This test shares the configuration provided for the standard\n    :doc:`../plugins/ssl_with_bad_version` test, please refer to its\n    documentation.\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: ssl.wrap_socket call with no SSL/TLS protocol version\n        specified, the default SSLv23 could be insecure, possible security\n        issue.\n           Severity: Low   Confidence: Medium\n           CWE: CWE-327 (https://cwe.mitre.org/data/definitions/327.html)\n           Location: ./examples/ssl-insecure-version.py:23\n        22\n        23  ssl.wrap_socket()\n        24\n\n    .. seealso::\n\n     - :func:`ssl_with_bad_version`\n     - :func:`ssl_with_bad_defaults`\n     - https://heartbleed.com/\n     - https://en.wikipedia.org/wiki/POODLE\n     - https://security.openstack.org/guidelines/dg_move-data-securely.html\n\n    .. versionadded:: 0.9.0\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"\n    if context.call_function_name_qual == \"ssl.wrap_socket\":\n        if context.check_call_arg_value(\"ssl_version\") is None:\n            # check_call_arg_value() returns False if the argument is found\n            # but does not match the supplied value (or the default None).\n            # It returns None if the arg_name passed doesn't exist. This\n            # tests for that (ssl_version is not specified).\n            return bandit.Issue(\n                severity=bandit.LOW,\n                confidence=bandit.MEDIUM,\n                cwe=issue.Cwe.BROKEN_CRYPTO,\n                text=\"ssl.wrap_socket call with no SSL/TLS protocol version \"\n                \"specified, the default SSLv23 could be insecure, \"\n                \"possible security issue.\",\n                lineno=context.get_lineno_for_call_arg(\"ssl_version\"),\n            )\n"
  },
  {
    "path": "bandit/plugins/jinja2_templates.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==========================================\nB701: Test for not auto escaping in jinja2\n==========================================\n\nJinja2 is a Python HTML templating system. It is typically used to build web\napplications, though appears in other places well, notably the Ansible\nautomation system. When configuring the Jinja2 environment, the option to use\nautoescaping on input can be specified. When autoescaping is enabled, Jinja2\nwill filter input strings to escape any HTML content submitted via template\nvariables. Without escaping HTML input the application becomes vulnerable to\nCross Site Scripting (XSS) attacks.\n\nUnfortunately, autoescaping is False by default. Thus this plugin test will\nwarn on omission of an autoescape setting, as well as an explicit setting of\nfalse. A HIGH severity warning is generated in either of these scenarios.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Using jinja2 templates with autoescape=False is dangerous and can\n    lead to XSS. Use autoescape=True to mitigate XSS vulnerabilities.\n       Severity: High   Confidence: High\n       CWE: CWE-94 (https://cwe.mitre.org/data/definitions/94.html)\n       Location: ./examples/jinja2_templating.py:11\n    10  templateEnv = jinja2.Environment(autoescape=False,\n        loader=templateLoader)\n    11  Environment(loader=templateLoader,\n    12              load=templateLoader,\n    13              autoescape=False)\n    14\n\n    >> Issue: By default, jinja2 sets autoescape to False. Consider using\n    autoescape=True or use the select_autoescape function to mitigate XSS\n    vulnerabilities.\n       Severity: High   Confidence: High\n       CWE: CWE-94 (https://cwe.mitre.org/data/definitions/94.html)\n       Location: ./examples/jinja2_templating.py:15\n    14\n    15  Environment(loader=templateLoader,\n    16              load=templateLoader)\n    17\n    18  Environment(autoescape=select_autoescape(['html', 'htm', 'xml']),\n    19              loader=templateLoader)\n\n\n.. seealso::\n\n - `OWASP XSS <https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)>`__\n - https://realpython.com/primer-on-jinja-templating/\n - https://jinja.palletsprojects.com/en/2.11.x/api/#autoescaping\n - https://security.openstack.org/guidelines/dg_cross-site-scripting-xss.html\n - https://cwe.mitre.org/data/definitions/94.html\n\n.. versionadded:: 0.10.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B701\")\ndef jinja2_autoescape_false(context):\n    # check type just to be safe\n    if isinstance(context.call_function_name_qual, str):\n        qualname_list = context.call_function_name_qual.split(\".\")\n        func = qualname_list[-1]\n        if \"jinja2\" in qualname_list and func == \"Environment\":\n            for node in ast.walk(context.node):\n                if isinstance(node, ast.keyword):\n                    # definite autoescape = False\n                    if getattr(node, \"arg\", None) == \"autoescape\" and (\n                        getattr(node.value, \"id\", None) == \"False\"\n                        or getattr(node.value, \"value\", None) is False\n                    ):\n                        return bandit.Issue(\n                            severity=bandit.HIGH,\n                            confidence=bandit.HIGH,\n                            cwe=issue.Cwe.CODE_INJECTION,\n                            text=\"Using jinja2 templates with autoescape=\"\n                            \"False is dangerous and can lead to XSS. \"\n                            \"Use autoescape=True or use the \"\n                            \"select_autoescape function to mitigate XSS \"\n                            \"vulnerabilities.\",\n                        )\n                    # found autoescape\n                    if getattr(node, \"arg\", None) == \"autoescape\":\n                        value = getattr(node, \"value\", None)\n                        if (\n                            getattr(value, \"id\", None) == \"True\"\n                            or getattr(value, \"value\", None) is True\n                        ):\n                            return\n                        # Check if select_autoescape function is used.\n                        elif isinstance(value, ast.Call) and (\n                            getattr(value.func, \"attr\", None)\n                            == \"select_autoescape\"\n                            or getattr(value.func, \"id\", None)\n                            == \"select_autoescape\"\n                        ):\n                            return\n                        else:\n                            return bandit.Issue(\n                                severity=bandit.HIGH,\n                                confidence=bandit.MEDIUM,\n                                cwe=issue.Cwe.CODE_INJECTION,\n                                text=\"Using jinja2 templates with autoescape=\"\n                                \"False is dangerous and can lead to XSS. \"\n                                \"Ensure autoescape=True or use the \"\n                                \"select_autoescape function to mitigate \"\n                                \"XSS vulnerabilities.\",\n                            )\n            # We haven't found a keyword named autoescape, indicating default\n            # behavior\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.CODE_INJECTION,\n                text=\"By default, jinja2 sets autoescape to False. Consider \"\n                \"using autoescape=True or use the select_autoescape \"\n                \"function to mitigate XSS vulnerabilities.\",\n            )\n"
  },
  {
    "path": "bandit/plugins/logging_config_insecure_listen.py",
    "content": "# Copyright (c) 2022 Rajesh Pangare\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n====================================================\nB612: Test for insecure use of logging.config.listen\n====================================================\n\nThis plugin test checks for the unsafe usage of the\n``logging.config.listen`` function. The logging.config.listen\nfunction provides the ability to listen for external\nconfiguration files on a socket server. Because portions of the\nconfiguration are passed through eval(), use of this function\nmay open its users to a security risk. While the function only\nbinds to a socket on localhost, and so does not accept connections\nfrom remote machines, there are scenarios where untrusted code\ncould be run under the account of the process which calls listen().\n\nlogging.config.listen provides the ability to verify bytes received\nacross the socket with signature verification or encryption/decryption.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B612:logging_config_listen] Use of insecure\n    logging.config.listen detected.\n       Severity: Medium   Confidence: High\n       CWE: CWE-94 (https://cwe.mitre.org/data/definitions/94.html)\n       Location: examples/logging_config_insecure_listen.py:3:4\n    2\n    3\tt = logging.config.listen(9999)\n\n.. seealso::\n\n - https://docs.python.org/3/library/logging.config.html#logging.config.listen\n\n.. versionadded:: 1.7.5\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B612\")\ndef logging_config_insecure_listen(context):\n    if (\n        context.call_function_name_qual == \"logging.config.listen\"\n        and \"verify\" not in context.call_keywords\n    ):\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.HIGH,\n            cwe=issue.Cwe.CODE_INJECTION,\n            text=\"Use of insecure logging.config.listen detected.\",\n        )\n"
  },
  {
    "path": "bandit/plugins/mako_templates.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n====================================\nB702: Test for use of mako templates\n====================================\n\nMako is a Python templating system often used to build web applications. It is\nthe default templating system used in Pylons and Pyramid. Unlike Jinja2 (an\nalternative templating system), Mako has no environment wide variable escaping\nmechanism. Because of this, all input variables must be carefully escaped\nbefore use to prevent possible vulnerabilities to Cross Site Scripting (XSS)\nattacks.\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Mako templates allow HTML/JS rendering by default and are\n    inherently open to XSS attacks. Ensure variables in all templates are\n    properly sanitized via the 'n', 'h' or 'x' flags (depending on context).\n    For example, to HTML escape the variable 'data' do ${ data |h }.\n       Severity: Medium   Confidence: High\n       CWE: CWE-80 (https://cwe.mitre.org/data/definitions/80.html)\n       Location: ./examples/mako_templating.py:10\n    9\n    10  mako.template.Template(\"hern\")\n    11  template.Template(\"hern\")\n\n\n.. seealso::\n\n - https://www.makotemplates.org/\n - `OWASP XSS <https://owasp.org/www-community/attacks/xss/>`__\n - https://security.openstack.org/guidelines/dg_cross-site-scripting-xss.html\n - https://cwe.mitre.org/data/definitions/80.html\n\n.. versionadded:: 0.10.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B702\")\ndef use_of_mako_templates(context):\n    # check type just to be safe\n    if isinstance(context.call_function_name_qual, str):\n        qualname_list = context.call_function_name_qual.split(\".\")\n        func = qualname_list[-1]\n        if \"mako\" in qualname_list and func == \"Template\":\n            # unlike Jinja2, mako does not have a template wide autoescape\n            # feature and thus each variable must be carefully sanitized.\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.BASIC_XSS,\n                text=\"Mako templates allow HTML/JS rendering by default and \"\n                \"are inherently open to XSS attacks. Ensure variables \"\n                \"in all templates are properly sanitized via the 'n', \"\n                \"'h' or 'x' flags (depending on context). For example, \"\n                \"to HTML escape the variable 'data' do ${ data |h }.\",\n            )\n"
  },
  {
    "path": "bandit/plugins/markupsafe_markup_xss.py",
    "content": "# Copyright (c) 2025 David Salvisberg\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n============================================\nB704: Potential XSS on markupsafe.Markup use\n============================================\n\n``markupsafe.Markup`` does not perform any escaping, so passing dynamic\ncontent, like f-strings, variables or interpolated strings will potentially\nlead to XSS vulnerabilities, especially if that data was submitted by users.\n\nInstead you should interpolate the resulting ``markupsafe.Markup`` object,\nwhich will perform escaping, or use ``markupsafe.escape``.\n\n\n**Config Options:**\n\nThis plugin allows you to specify additional callable that should be treated\nlike ``markupsafe.Markup``. By default we recognize ``flask.Markup`` as\nan alias, but there are other subclasses or similar classes in the wild\nthat you may wish to treat the same.\n\nAdditionally there is a whitelist for callable names, whose result may\nbe safely passed into ``markupsafe.Markup``. This is useful for escape\nfunctions like e.g. ``bleach.clean`` which don't themselves return\n``markupsafe.Markup``, so they need to be wrapped. Take care when using\nthis setting, since incorrect use may introduce false negatives.\n\nThese two options can be set in a shared configuration section\n`markupsafe_xss`.\n\n\n.. code-block:: yaml\n\n    markupsafe_xss:\n        # Recognize additional aliases\n        extend_markup_names:\n            - webhelpers.html.literal\n            - my_package.Markup\n\n        # Allow the output of these functions to pass into Markup\n        allowed_calls:\n            - bleach.clean\n            - my_package.sanitize\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B704:markupsafe_markup_xss] Potential XSS with\n       ``markupsafe.Markup`` detected. Do not use ``Markup``\n       on untrusted data.\n       Severity: Medium   Confidence: High\n       CWE: CWE-79 (https://cwe.mitre.org/data/definitions/79.html)\n       Location: ./examples/markupsafe_markup_xss.py:5:0\n    4       content = \"<script>alert('Hello, world!')</script>\"\n    5       Markup(f\"unsafe {content}\")\n    6       flask.Markup(\"unsafe {}\".format(content))\n\n.. seealso::\n\n - https://pypi.org/project/MarkupSafe/\n - https://markupsafe.palletsprojects.com/en/stable/escaping/#markupsafe.Markup\n - https://cwe.mitre.org/data/definitions/79.html\n\n.. versionadded:: 1.8.3\n\n\"\"\"\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\nfrom bandit.core.utils import get_call_name\n\n\ndef gen_config(name):\n    if name == \"markupsafe_xss\":\n        return {\n            \"extend_markup_names\": [],\n            \"allowed_calls\": [],\n        }\n\n\n@test.takes_config(\"markupsafe_xss\")\n@test.checks(\"Call\")\n@test.test_id(\"B704\")\ndef markupsafe_markup_xss(context, config):\n\n    qualname = context.call_function_name_qual\n    if qualname not in (\"markupsafe.Markup\", \"flask.Markup\"):\n        if qualname not in config.get(\"extend_markup_names\", []):\n            # not a Markup call\n            return None\n\n    args = context.node.args\n    if not args or isinstance(args[0], ast.Constant):\n        # both no arguments and a constant are fine\n        return None\n\n    allowed_calls = config.get(\"allowed_calls\", [])\n    if (\n        allowed_calls\n        and isinstance(args[0], ast.Call)\n        and get_call_name(args[0], context.import_aliases) in allowed_calls\n    ):\n        # the argument contains a whitelisted call\n        return None\n\n    return bandit.Issue(\n        severity=bandit.MEDIUM,\n        confidence=bandit.HIGH,\n        cwe=issue.Cwe.XSS,\n        text=f\"Potential XSS with ``{qualname}`` detected. Do \"\n        f\"not use ``{context.call_function_name}`` on untrusted data.\",\n    )\n"
  },
  {
    "path": "bandit/plugins/pytorch_load.py",
    "content": "# Copyright (c) 2024 Stacklok, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==================================\nB614: Test for unsafe PyTorch load\n==================================\n\nThis plugin checks for unsafe use of `torch.load` and\n`torch.serialization.load`. Using `torch.load` or\n`torch.serialization.load` with untrusted data can lead to arbitrary\ncode execution. There are two safe alternatives:\n\n1. Use `torch.load` with `weights_only=True` where only tensor data is\n   extracted, and no arbitrary Python objects are deserialized\n2. Use the `safetensors` library from huggingface, which provides a safe\n   deserialization mechanism\n\nWith `weights_only=True`, PyTorch enforces a strict type check, ensuring\nthat only torch.Tensor objects are loaded.\n\n:Example:\n\n.. code-block:: none\n\n        >> Issue: Use of unsafe PyTorch load\n        Severity: Medium   Confidence: High\n        CWE: CWE-502 (https://cwe.mitre.org/data/definitions/502.html)\n        Location: examples/pytorch_load_save.py:8\n        7    loaded_model.load_state_dict(torch.load('model_weights.pth'))\n        8    another_model.load_state_dict(torch.load('model_weights.pth',\n                map_location='cpu'))\n        9\n        10   print(\"Model loaded successfully!\")\n\n.. seealso::\n\n     - https://cwe.mitre.org/data/definitions/502.html\n     - https://pytorch.org/docs/stable/generated/torch.load.html#torch.load\n     - https://github.com/huggingface/safetensors\n\n.. versionadded:: 1.7.10\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B614\")\ndef pytorch_load(context):\n    \"\"\"\n    This plugin checks for unsafe use of `torch.load` and\n    `torch.serialization.load`. Using `torch.load` or\n    `torch.serialization.load` with untrusted data can lead to\n    arbitrary code execution. The safe alternative is to use\n    `weights_only=True` or the safetensors library.\n    \"\"\"\n    imported = context.is_module_imported_exact(\"torch\")\n    qualname = context.call_function_name_qual\n    if not imported and isinstance(qualname, str):\n        return\n\n    if qualname in {\"torch.load\", \"torch.serialization.load\"}:\n        # For torch.load, check if weights_only=True is specified\n        weights_only = context.get_call_arg_value(\"weights_only\")\n        if weights_only == \"True\" or weights_only is True:\n            return\n\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.HIGH,\n            text=\"Use of unsafe PyTorch load\",\n            cwe=issue.Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA,\n            lineno=context.get_lineno_for_call_arg(\"load\"),\n        )\n"
  },
  {
    "path": "bandit/plugins/request_without_timeout.py",
    "content": "# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=======================================\nB113: Test for missing requests timeout\n=======================================\n\nThis plugin test checks for ``requests`` or ``httpx`` calls without a timeout\nspecified.\n\nNearly all production code should use this parameter in nearly all requests,\nFailure to do so can cause your program to hang indefinitely.\n\nWhen request methods are used without the timeout parameter set,\nBandit will return a MEDIUM severity error.\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B113:request_without_timeout] Call to requests without timeout\n       Severity: Medium   Confidence: Low\n       CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html)\n       More Info: https://bandit.readthedocs.io/en/latest/plugins/b113_request_without_timeout.html\n       Location: examples/requests-missing-timeout.py:3:0\n    2\n    3\trequests.get('https://gmail.com')\n    4\trequests.get('https://gmail.com', timeout=None)\n\n    --------------------------------------------------\n    >> Issue: [B113:request_without_timeout] Call to requests with timeout set to None\n       Severity: Medium   Confidence: Low\n       CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html)\n       More Info: https://bandit.readthedocs.io/en/latest/plugins/b113_request_without_timeout.html\n       Location: examples/requests-missing-timeout.py:4:0\n    3\trequests.get('https://gmail.com')\n    4\trequests.get('https://gmail.com', timeout=None)\n    5\trequests.get('https://gmail.com', timeout=5)\n\n.. seealso::\n\n - https://requests.readthedocs.io/en/latest/user/advanced/#timeouts\n\n.. versionadded:: 1.7.5\n\n.. versionchanged:: 1.7.10\n    Added check for httpx module\n\n\"\"\"  # noqa: E501\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B113\")\ndef request_without_timeout(context):\n    HTTP_VERBS = {\"get\", \"options\", \"head\", \"post\", \"put\", \"patch\", \"delete\"}\n    HTTPX_ATTRS = {\"request\", \"stream\", \"Client\", \"AsyncClient\"} | HTTP_VERBS\n    qualname = context.call_function_name_qual.split(\".\")[0]\n\n    if qualname == \"requests\" and context.call_function_name in HTTP_VERBS:\n        # check for missing timeout\n        if context.check_call_arg_value(\"timeout\") is None:\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.LOW,\n                cwe=issue.Cwe.UNCONTROLLED_RESOURCE_CONSUMPTION,\n                text=f\"Call to {qualname} without timeout\",\n            )\n    if (\n        qualname == \"requests\"\n        and context.call_function_name in HTTP_VERBS\n        or qualname == \"httpx\"\n        and context.call_function_name in HTTPX_ATTRS\n    ):\n        # check for timeout=None\n        if context.check_call_arg_value(\"timeout\", \"None\"):\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.LOW,\n                cwe=issue.Cwe.UNCONTROLLED_RESOURCE_CONSUMPTION,\n                text=f\"Call to {qualname} with timeout set to None\",\n            )\n"
  },
  {
    "path": "bandit/plugins/snmp_security_check.py",
    "content": "#\n# Copyright (c) 2018 SolarWinds, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B508\")\ndef snmp_insecure_version_check(context):\n    \"\"\"**B508: Checking for insecure SNMP versions**\n\n    This test is for checking for the usage of insecure SNMP version like\n      v1, v2c\n\n    Please update your code to use more secure versions of SNMP.\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B508:snmp_insecure_version_check] The use of SNMPv1 and\n           SNMPv2 is insecure. You should use SNMPv3 if able.\n           Severity: Medium Confidence: High\n           CWE: CWE-319 (https://cwe.mitre.org/data/definitions/319.html)\n           Location: examples/snmp.py:4:4\n           More Info: https://bandit.readthedocs.io/en/latest/plugins/b508_snmp_insecure_version_check.html\n        3   # SHOULD FAIL\n        4   a = CommunityData('public', mpModel=0)\n        5   # SHOULD FAIL\n\n    .. seealso::\n\n     - http://snmplabs.com/pysnmp/examples/hlapi/asyncore/sync/manager/cmdgen/snmp-versions.html\n     - https://cwe.mitre.org/data/definitions/319.html\n\n    .. versionadded:: 1.7.2\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n\n    if context.call_function_name_qual == \"pysnmp.hlapi.CommunityData\":\n        # We called community data. Lets check our args\n        if context.check_call_arg_value(\n            \"mpModel\", 0\n        ) or context.check_call_arg_value(\"mpModel\", 1):\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.CLEARTEXT_TRANSMISSION,\n                text=\"The use of SNMPv1 and SNMPv2 is insecure. \"\n                \"You should use SNMPv3 if able.\",\n                lineno=context.get_lineno_for_call_arg(\"CommunityData\"),\n            )\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B509\")\ndef snmp_crypto_check(context):\n    \"\"\"**B509: Checking for weak cryptography**\n\n    This test is for checking for the usage of insecure SNMP cryptography:\n      v3 using noAuthNoPriv.\n\n    Please update your code to use more secure versions of SNMP. For example:\n\n    Instead of:\n      `CommunityData('public', mpModel=0)`\n\n    Use (Defaults to usmHMACMD5AuthProtocol and usmDESPrivProtocol\n      `UsmUserData(\"securityName\", \"authName\", \"privName\")`\n\n    :Example:\n\n    .. code-block:: none\n\n        >> Issue: [B509:snmp_crypto_check] You should not use SNMPv3 without encryption. noAuthNoPriv & authNoPriv is insecure\n           Severity: Medium CWE: CWE-319 (https://cwe.mitre.org/data/definitions/319.html) Confidence: High\n           Location: examples/snmp.py:6:11\n           More Info: https://bandit.readthedocs.io/en/latest/plugins/b509_snmp_crypto_check.html\n        5   # SHOULD FAIL\n        6   insecure = UsmUserData(\"securityName\")\n        7   # SHOULD FAIL\n\n    .. seealso::\n\n     - http://snmplabs.com/pysnmp/examples/hlapi/asyncore/sync/manager/cmdgen/snmp-versions.html\n     - https://cwe.mitre.org/data/definitions/319.html\n\n    .. versionadded:: 1.7.2\n\n    .. versionchanged:: 1.7.3\n        CWE information added\n\n    \"\"\"  # noqa: E501\n\n    if context.call_function_name_qual == \"pysnmp.hlapi.UsmUserData\":\n        if context.call_args_count < 3:\n            return bandit.Issue(\n                severity=bandit.MEDIUM,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.CLEARTEXT_TRANSMISSION,\n                text=\"You should not use SNMPv3 without encryption. \"\n                \"noAuthNoPriv & authNoPriv is insecure\",\n                lineno=context.get_lineno_for_call_arg(\"UsmUserData\"),\n            )\n"
  },
  {
    "path": "bandit/plugins/ssh_no_host_key_verification.py",
    "content": "# Copyright (c) 2018 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n==========================================\nB507: Test for missing host key validation\n==========================================\n\nEncryption in general is typically critical to the security of many\napplications.  Using SSH can greatly increase security by guaranteeing the\nidentity of the party you are communicating with.  This is accomplished by one\nor both parties presenting trusted host keys during the connection\ninitialization phase of SSH.\n\nWhen paramiko methods are used, host keys are verified by default. If host key\nverification is disabled, Bandit will return a HIGH severity error.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B507:ssh_no_host_key_verification] Paramiko call with policy set\n    to automatically trust the unknown host key.\n    Severity: High   Confidence: Medium\n    CWE: CWE-295 (https://cwe.mitre.org/data/definitions/295.html)\n    Location: examples/no_host_key_verification.py:4\n    3   ssh_client = client.SSHClient()\n    4   ssh_client.set_missing_host_key_policy(client.AutoAddPolicy)\n    5   ssh_client.set_missing_host_key_policy(client.WarningPolicy)\n\n\n.. versionadded:: 1.5.1\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.checks(\"Call\")\n@test.test_id(\"B507\")\ndef ssh_no_host_key_verification(context):\n    if (\n        context.is_module_imported_like(\"paramiko\")\n        and context.call_function_name == \"set_missing_host_key_policy\"\n        and context.node.args\n    ):\n        policy_argument = context.node.args[0]\n\n        policy_argument_value = None\n        if isinstance(policy_argument, ast.Attribute):\n            policy_argument_value = policy_argument.attr\n        elif isinstance(policy_argument, ast.Name):\n            policy_argument_value = policy_argument.id\n        elif isinstance(policy_argument, ast.Call):\n            if isinstance(policy_argument.func, ast.Attribute):\n                policy_argument_value = policy_argument.func.attr\n            elif isinstance(policy_argument.func, ast.Name):\n                policy_argument_value = policy_argument.func.id\n\n        if policy_argument_value in [\"AutoAddPolicy\", \"WarningPolicy\"]:\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.MEDIUM,\n                cwe=issue.Cwe.IMPROPER_CERT_VALIDATION,\n                text=\"Paramiko call with policy set to automatically trust \"\n                \"the unknown host key.\",\n                lineno=context.get_lineno_for_call_arg(\n                    \"set_missing_host_key_policy\"\n                ),\n            )\n"
  },
  {
    "path": "bandit/plugins/tarfile_unsafe_members.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\n#\nr\"\"\"\n=================================\nB202: Test for tarfile.extractall\n=================================\n\nThis plugin will look for usage of ``tarfile.extractall()``\n\nSeverity are set as follows:\n\n* ``tarfile.extractall(members=function(tarfile))`` - LOW\n* ``tarfile.extractall(members=?)`` - member is not a function - MEDIUM\n* ``tarfile.extractall()`` - members from the archive is trusted - HIGH\n\nUse ``tarfile.extractall(members=function_name)`` and define a function\nthat will inspect each member. Discard files that contain a directory\ntraversal sequences such as ``../`` or ``\\..`` along with all special filetypes\nunless you explicitly need them.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B202:tarfile_unsafe_members] tarfile.extractall used without\n    any validation. You should check members and discard dangerous ones\n    Severity: High   Confidence: High\n    CWE: CWE-22 (https://cwe.mitre.org/data/definitions/22.html)\n    Location: examples/tarfile_extractall.py:8\n    More Info:\n    https://bandit.readthedocs.io/en/latest/plugins/b202_tarfile_unsafe_members.html\n    7\t    tar = tarfile.open(filename)\n    8\t    tar.extractall(path=tempfile.mkdtemp())\n    9\t    tar.close()\n\n\n.. seealso::\n\n - https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall\n - https://docs.python.org/3/library/tarfile.html#tarfile.TarInfo\n\n.. versionadded:: 1.7.5\n\n.. versionchanged:: 1.7.8\n    Added check for filter parameter\n\n\"\"\"\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef exec_issue(level, members=\"\"):\n    if level == bandit.LOW:\n        return bandit.Issue(\n            severity=bandit.LOW,\n            confidence=bandit.LOW,\n            cwe=issue.Cwe.PATH_TRAVERSAL,\n            text=\"Usage of tarfile.extractall(members=function(tarfile)). \"\n            \"Make sure your function properly discards dangerous members \"\n            \"{members}).\".format(members=members),\n        )\n    elif level == bandit.MEDIUM:\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.MEDIUM,\n            cwe=issue.Cwe.PATH_TRAVERSAL,\n            text=\"Found tarfile.extractall(members=?) but couldn't \"\n            \"identify the type of members. \"\n            \"Check if the members were properly validated \"\n            \"{members}).\".format(members=members),\n        )\n    else:\n        return bandit.Issue(\n            severity=bandit.HIGH,\n            confidence=bandit.HIGH,\n            cwe=issue.Cwe.PATH_TRAVERSAL,\n            text=\"tarfile.extractall used without any validation. \"\n            \"Please check and discard dangerous members.\",\n        )\n\n\ndef get_members_value(context):\n    for keyword in context.node.keywords:\n        if keyword.arg == \"members\":\n            arg = keyword.value\n            if isinstance(arg, ast.Call):\n                return {\"Function\": arg.func.id}\n            else:\n                value = arg.id if isinstance(arg, ast.Name) else arg\n                return {\"Other\": value}\n\n\ndef is_filter_data(context):\n    for keyword in context.node.keywords:\n        if keyword.arg == \"filter\":\n            arg = keyword.value\n            return isinstance(arg, ast.Constant) and arg.value == \"data\"\n\n\n@test.test_id(\"B202\")\n@test.checks(\"Call\")\ndef tarfile_unsafe_members(context):\n    if all(\n        [\n            context.is_module_imported_exact(\"tarfile\"),\n            \"extractall\" in context.call_function_name,\n        ]\n    ):\n        if \"filter\" in context.call_keywords and is_filter_data(context):\n            return None\n        if \"members\" in context.call_keywords:\n            members = get_members_value(context)\n            if \"Function\" in members:\n                return exec_issue(bandit.LOW, members)\n            else:\n                return exec_issue(bandit.MEDIUM, members)\n        return exec_issue(bandit.HIGH)\n"
  },
  {
    "path": "bandit/plugins/trojansource.py",
    "content": "#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=====================================================\nB613: TrojanSource - Bidirectional control characters\n=====================================================\n\nThis plugin checks for the presence of unicode bidirectional control characters\nin Python source files. Those characters can be embedded in comments and strings\nto reorder source code characters in a way that changes its logic.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B613:trojansource] A Python source file contains bidirectional control characters ('\\u202e').\n       Severity: High   Confidence: Medium\n       CWE: CWE-838 (https://cwe.mitre.org/data/definitions/838.html)\n       More Info: https://bandit.readthedocs.io/en/1.7.5/plugins/b113_trojansource.html\n       Location: examples/trojansource.py:4:25\n     3  \taccess_level = \"user\"\n     4\t    if access_level != 'none‮⁦': # Check if admin ⁩⁦' and access_level != 'user\n     5\t        print(\"You are an admin.\\n\")\n\n.. seealso::\n\n - https://trojansource.codes/\n - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574\n\n.. versionadded:: 1.7.10\n\n\"\"\"  # noqa: E501\nfrom tokenize import detect_encoding\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\nBIDI_CHARACTERS = (\n    \"\\u202a\",\n    \"\\u202b\",\n    \"\\u202c\",\n    \"\\u202d\",\n    \"\\u202e\",\n    \"\\u2066\",\n    \"\\u2067\",\n    \"\\u2068\",\n    \"\\u2069\",\n    \"\\u200f\",\n)\n\n\n@test.test_id(\"B613\")\n@test.checks(\"File\")\ndef trojansource(context):\n    src_data = context.file_data\n    src_data.seek(0)\n    encoding, _ = detect_encoding(src_data.readline)\n    src_data.seek(0)\n    for lineno, line in enumerate(\n        src_data.read().decode(encoding).splitlines(), start=1\n    ):\n        for char in BIDI_CHARACTERS:\n            try:\n                col_offset = line.index(char) + 1\n            except ValueError:\n                continue\n            text = (\n                \"A Python source file contains bidirectional\"\n                \" control characters (%r).\" % char\n            )\n            b_issue = bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.MEDIUM,\n                cwe=issue.Cwe.INAPPROPRIATE_ENCODING_FOR_OUTPUT_CONTEXT,\n                text=text,\n                lineno=lineno,\n                col_offset=col_offset,\n            )\n            b_issue.linerange = [lineno]\n            return b_issue\n"
  },
  {
    "path": "bandit/plugins/try_except_continue.py",
    "content": "# Copyright 2016 IBM Corp.\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=============================================\nB112: Test for a continue in the except block\n=============================================\n\nErrors in Python code bases are typically communicated using ``Exceptions``.\nAn exception object is 'raised' in the event of an error and can be 'caught' at\na later point in the program, typically some error handling or logging action\nwill then be performed.\n\nHowever, it is possible to catch an exception and silently ignore it while in\na loop. This is illustrated with the following example\n\n.. code-block:: python\n\n    while keep_going:\n      try:\n        do_some_stuff()\n      except Exception:\n        continue\n\nThis pattern is considered bad practice in general, but also represents a\npotential security issue. A larger than normal volume of errors from a service\ncan indicate an attempt is being made to disrupt or interfere with it. Thus\nerrors should, at the very least, be logged.\n\nThere are rare situations where it is desirable to suppress errors, but this is\ntypically done with specific exception types, rather than the base Exception\nclass (or no type). To accommodate this, the test may be configured to ignore\n'try, except, continue' where the exception is typed. For example, the\nfollowing would not generate a warning if the configuration option\n``checked_typed_exception`` is set to False:\n\n.. code-block:: python\n\n    while keep_going:\n      try:\n        do_some_stuff()\n      except ZeroDivisionError:\n        continue\n\n**Config Options:**\n\n.. code-block:: yaml\n\n    try_except_continue:\n      check_typed_exception: True\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Try, Except, Continue detected.\n       Severity: Low   Confidence: High\n       CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)\n       Location: ./examples/try_except_continue.py:5\n    4            a = i\n    5        except:\n    6            continue\n\n.. seealso::\n\n - https://security.openstack.org\n - https://cwe.mitre.org/data/definitions/703.html\n\n.. versionadded:: 1.0.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef gen_config(name):\n    if name == \"try_except_continue\":\n        return {\"check_typed_exception\": False}\n\n\n@test.takes_config\n@test.checks(\"ExceptHandler\")\n@test.test_id(\"B112\")\ndef try_except_continue(context, config):\n    node = context.node\n    if len(node.body) == 1:\n        if (\n            not config[\"check_typed_exception\"]\n            and node.type is not None\n            and getattr(node.type, \"id\", None) != \"Exception\"\n        ):\n            return\n\n        if isinstance(node.body[0], ast.Continue):\n            return bandit.Issue(\n                severity=bandit.LOW,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.IMPROPER_CHECK_OF_EXCEPT_COND,\n                text=(\"Try, Except, Continue detected.\"),\n            )\n"
  },
  {
    "path": "bandit/plugins/try_except_pass.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=========================================\nB110: Test for a pass in the except block\n=========================================\n\nErrors in Python code bases are typically communicated using ``Exceptions``.\nAn exception object is 'raised' in the event of an error and can be 'caught' at\na later point in the program, typically some error handling or logging action\nwill then be performed.\n\nHowever, it is possible to catch an exception and silently ignore it. This is\nillustrated with the following example\n\n.. code-block:: python\n\n    try:\n      do_some_stuff()\n    except Exception:\n      pass\n\nThis pattern is considered bad practice in general, but also represents a\npotential security issue. A larger than normal volume of errors from a service\ncan indicate an attempt is being made to disrupt or interfere with it. Thus\nerrors should, at the very least, be logged.\n\nThere are rare situations where it is desirable to suppress errors, but this is\ntypically done with specific exception types, rather than the base Exception\nclass (or no type). To accommodate this, the test may be configured to ignore\n'try, except, pass' where the exception is typed. For example, the following\nwould not generate a warning if the configuration option\n``checked_typed_exception`` is set to False:\n\n.. code-block:: python\n\n    try:\n      do_some_stuff()\n    except ZeroDivisionError:\n      pass\n\n**Config Options:**\n\n.. code-block:: yaml\n\n    try_except_pass:\n      check_typed_exception: True\n\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Try, Except, Pass detected.\n       Severity: Low   Confidence: High\n       CWE: CWE-703 (https://cwe.mitre.org/data/definitions/703.html)\n       Location: ./examples/try_except_pass.py:4\n    3        a = 1\n    4    except:\n    5        pass\n\n.. seealso::\n\n - https://security.openstack.org\n - https://cwe.mitre.org/data/definitions/703.html\n\n.. versionadded:: 0.13.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport ast\n\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef gen_config(name):\n    if name == \"try_except_pass\":\n        return {\"check_typed_exception\": False}\n\n\n@test.takes_config\n@test.checks(\"ExceptHandler\")\n@test.test_id(\"B110\")\ndef try_except_pass(context, config):\n    node = context.node\n    if len(node.body) == 1:\n        if (\n            not config[\"check_typed_exception\"]\n            and node.type is not None\n            and getattr(node.type, \"id\", None) != \"Exception\"\n        ):\n            return\n\n        if isinstance(node.body[0], ast.Pass):\n            return bandit.Issue(\n                severity=bandit.LOW,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.IMPROPER_CHECK_OF_EXCEPT_COND,\n                text=(\"Try, Except, Pass detected.\"),\n            )\n"
  },
  {
    "path": "bandit/plugins/weak_cryptographic_key.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n=========================================\nB505: Test for weak cryptographic key use\n=========================================\n\nAs computational power increases, so does the ability to break ciphers with\nsmaller key lengths. The recommended key length size for RSA and DSA algorithms\nis 2048 and higher. 1024 bits and below are now considered breakable. EC key\nlength sizes are recommended to be 224 and higher with 160 and below considered\nbreakable. This plugin test checks for use of any key less than those limits\nand returns a high severity error if lower than the lower threshold and a\nmedium severity error for those lower than the higher threshold.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: DSA key sizes below 1024 bits are considered breakable.\n       Severity: High   Confidence: High\n       CWE: CWE-326 (https://cwe.mitre.org/data/definitions/326.html)\n       Location: examples/weak_cryptographic_key_sizes.py:36\n    35  # Also incorrect: without keyword args\n    36  dsa.generate_private_key(512,\n    37                           backends.default_backend())\n    38  rsa.generate_private_key(3,\n\n.. seealso::\n\n - https://csrc.nist.gov/publications/detail/sp/800-131a/rev-2/final\n - https://security.openstack.org/guidelines/dg_strong-crypto.html\n - https://cwe.mitre.org/data/definitions/326.html\n\n.. versionadded:: 0.14.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\ndef gen_config(name):\n    if name == \"weak_cryptographic_key\":\n        return {\n            \"weak_key_size_dsa_high\": 1024,\n            \"weak_key_size_dsa_medium\": 2048,\n            \"weak_key_size_rsa_high\": 1024,\n            \"weak_key_size_rsa_medium\": 2048,\n            \"weak_key_size_ec_high\": 160,\n            \"weak_key_size_ec_medium\": 224,\n        }\n\n\ndef _classify_key_size(config, key_type, key_size):\n    if isinstance(key_size, str):\n        # size provided via a variable - can't process it at the moment\n        return\n\n    key_sizes = {\n        \"DSA\": [\n            (config[\"weak_key_size_dsa_high\"], bandit.HIGH),\n            (config[\"weak_key_size_dsa_medium\"], bandit.MEDIUM),\n        ],\n        \"RSA\": [\n            (config[\"weak_key_size_rsa_high\"], bandit.HIGH),\n            (config[\"weak_key_size_rsa_medium\"], bandit.MEDIUM),\n        ],\n        \"EC\": [\n            (config[\"weak_key_size_ec_high\"], bandit.HIGH),\n            (config[\"weak_key_size_ec_medium\"], bandit.MEDIUM),\n        ],\n    }\n\n    for size, level in key_sizes[key_type]:\n        if key_size < size:\n            return bandit.Issue(\n                severity=level,\n                confidence=bandit.HIGH,\n                cwe=issue.Cwe.INADEQUATE_ENCRYPTION_STRENGTH,\n                text=\"%s key sizes below %d bits are considered breakable. \"\n                % (key_type, size),\n            )\n\n\ndef _weak_crypto_key_size_cryptography_io(context, config):\n    func_key_type = {\n        \"cryptography.hazmat.primitives.asymmetric.dsa.\"\n        \"generate_private_key\": \"DSA\",\n        \"cryptography.hazmat.primitives.asymmetric.rsa.\"\n        \"generate_private_key\": \"RSA\",\n        \"cryptography.hazmat.primitives.asymmetric.ec.\"\n        \"generate_private_key\": \"EC\",\n    }\n    arg_position = {\n        \"DSA\": 0,\n        \"RSA\": 1,\n        \"EC\": 0,\n    }\n    key_type = func_key_type.get(context.call_function_name_qual)\n    if key_type in [\"DSA\", \"RSA\"]:\n        key_size = (\n            context.get_call_arg_value(\"key_size\")\n            or context.get_call_arg_at_position(arg_position[key_type])\n            or 2048\n        )\n        return _classify_key_size(config, key_type, key_size)\n    elif key_type == \"EC\":\n        curve_key_sizes = {\n            \"SECT571K1\": 571,\n            \"SECT571R1\": 570,\n            \"SECP521R1\": 521,\n            \"BrainpoolP512R1\": 512,\n            \"SECT409K1\": 409,\n            \"SECT409R1\": 409,\n            \"BrainpoolP384R1\": 384,\n            \"SECP384R1\": 384,\n            \"SECT283K1\": 283,\n            \"SECT283R1\": 283,\n            \"BrainpoolP256R1\": 256,\n            \"SECP256K1\": 256,\n            \"SECP256R1\": 256,\n            \"SECT233K1\": 233,\n            \"SECT233R1\": 233,\n            \"SECP224R1\": 224,\n            \"SECP192R1\": 192,\n            \"SECT163K1\": 163,\n            \"SECT163R2\": 163,\n        }\n        curve = context.get_call_arg_value(\"curve\") or (\n            len(context.call_args) > arg_position[key_type]\n            and context.call_args[arg_position[key_type]]\n        )\n        key_size = curve_key_sizes[curve] if curve in curve_key_sizes else 224\n        return _classify_key_size(config, key_type, key_size)\n\n\ndef _weak_crypto_key_size_pycrypto(context, config):\n    func_key_type = {\n        \"Crypto.PublicKey.DSA.generate\": \"DSA\",\n        \"Crypto.PublicKey.RSA.generate\": \"RSA\",\n        \"Cryptodome.PublicKey.DSA.generate\": \"DSA\",\n        \"Cryptodome.PublicKey.RSA.generate\": \"RSA\",\n    }\n    key_type = func_key_type.get(context.call_function_name_qual)\n    if key_type:\n        key_size = (\n            context.get_call_arg_value(\"bits\")\n            or context.get_call_arg_at_position(0)\n            or 2048\n        )\n        return _classify_key_size(config, key_type, key_size)\n\n\n@test.takes_config\n@test.checks(\"Call\")\n@test.test_id(\"B505\")\ndef weak_cryptographic_key(context, config):\n    return _weak_crypto_key_size_cryptography_io(\n        context, config\n    ) or _weak_crypto_key_size_pycrypto(context, config)\n"
  },
  {
    "path": "bandit/plugins/yaml_load.py",
    "content": "#\n# Copyright (c) 2016 Rackspace, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nr\"\"\"\n===============================\nB506: Test for use of yaml load\n===============================\n\nThis plugin test checks for the unsafe usage of the ``yaml.load`` function from\nthe PyYAML package. The yaml.load function provides the ability to construct\nan arbitrary Python object, which may be dangerous if you receive a YAML\ndocument from an untrusted source. The function yaml.safe_load limits this\nability to simple Python objects like integers or lists.\n\nPlease see\nhttps://pyyaml.org/wiki/PyYAMLDocumentation#LoadingYAML for more information\non ``yaml.load`` and yaml.safe_load\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [yaml_load] Use of unsafe yaml load. Allows instantiation of\n       arbitrary objects. Consider yaml.safe_load().\n       Severity: Medium   Confidence: High\n       CWE: CWE-20 (https://cwe.mitre.org/data/definitions/20.html)\n       Location: examples/yaml_load.py:5\n    4 ystr = yaml.dump({'a' : 1, 'b' : 2, 'c' : 3})\n    5 y = yaml.load(ystr)\n    6 yaml.dump(y)\n\n.. seealso::\n\n - https://pyyaml.org/wiki/PyYAMLDocumentation#LoadingYAML\n - https://cwe.mitre.org/data/definitions/20.html\n\n.. versionadded:: 1.0.0\n\n.. versionchanged:: 1.7.3\n    CWE information added\n\n\"\"\"\nimport bandit\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\n\n\n@test.test_id(\"B506\")\n@test.checks(\"Call\")\ndef yaml_load(context):\n    imported = context.is_module_imported_exact(\"yaml\")\n    qualname = context.call_function_name_qual\n    if not imported and isinstance(qualname, str):\n        return\n\n    qualname_list = qualname.split(\".\")\n    func = qualname_list[-1]\n    if all(\n        [\n            \"yaml\" in qualname_list,\n            func == \"load\",\n            not context.check_call_arg_value(\"Loader\", \"SafeLoader\"),\n            not context.check_call_arg_value(\"Loader\", \"CSafeLoader\"),\n            not context.get_call_arg_at_position(1) == \"SafeLoader\",\n            not context.get_call_arg_at_position(1) == \"CSafeLoader\",\n        ]\n    ):\n        return bandit.Issue(\n            severity=bandit.MEDIUM,\n            confidence=bandit.HIGH,\n            cwe=issue.Cwe.IMPROPER_INPUT_VALIDATION,\n            text=\"Use of unsafe yaml load. Allows instantiation of\"\n            \" arbitrary objects. Consider yaml.safe_load().\",\n            lineno=context.node.lineno,\n        )\n"
  },
  {
    "path": "doc/requirements.txt",
    "content": "# The order of packages is significant, because pip processes them in the order\n# of appearance. Changing the order has an impact on the overall integration\n# process, which may cause wedges in the gate later.\nsphinx>=4.0.0 # BSD\nsphinx-rtd-theme>=0.3.0\nsphinx-copybutton\n"
  },
  {
    "path": "doc/source/blacklists/blacklist_calls.rst",
    "content": "---------------\nblacklist_calls\n---------------\n\n.. automodule:: bandit.blacklists.calls\n   :no-index:\n"
  },
  {
    "path": "doc/source/blacklists/blacklist_imports.rst",
    "content": "-----------------\nblacklist_imports\n-----------------\n\n.. automodule:: bandit.blacklists.imports\n   :no-index:\n"
  },
  {
    "path": "doc/source/blacklists/index.rst",
    "content": "Blacklist Plugins\n=================\n\nBandit supports built in functionality to implement blacklisting of imports and\nfunction calls, this functionality is provided by built in test 'B001'. This\ntest may be filtered as per normal plugin filtering rules.\n\nThe exact calls and imports that are blacklisted, and the issues reported, are\ncontrolled by plugin methods with the entry point 'bandit.blacklists' and can\nbe extended by third party plugins if desired. Blacklist plugins will be\ndiscovered by Bandit at startup and called. The returned results are combined\ninto the final data set, subject to Bandit's normal test include/exclude rules\nallowing for fine grained control over blacklisted items. By convention,\nblacklisted calls should have IDs in the B3xx range and imports should have IDs\nin the B4xx range.\n\nPlugin functions should return a dictionary mapping AST node types to\nlists of blacklist data. Currently the following node types are supported:\n\n- Call, used for blacklisting calls.\n- Import, used for blacklisting module imports (this also implicitly tests\n  ImportFrom and Call nodes where the invoked function is Pythons built in\n  '__import__()' method).\n\nItems in the data lists are Python dictionaries with the following structure:\n\n+-------------+----------------------------------------------------+\n| key         | data meaning                                       |\n+=============+====================================================+\n| 'name'      | The issue name string.                             |\n+-------------+----------------------------------------------------+\n| 'id'        | The bandit ID of the check, this must be unique    |\n|             | and is used for filtering blacklist checks.        |\n+-------------+----------------------------------------------------+\n| 'qualnames' | A Python list of fully qualified name strings.     |\n+-------------+----------------------------------------------------+\n| 'message'   | The issue message reported, this is a string that  |\n|             | may contain the token '{name}' that will be        |\n|             | substituted with the matched qualname in the final |\n|             | report.                                            |\n+-------------+----------------------------------------------------+\n| 'level'     | The severity level reported.                       |\n+-------------+----------------------------------------------------+\n\nA utility method bandit.blacklists.utils.build_conf_dict is provided to aid\nbuilding these dictionaries.\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [B317:blacklist] Using xml.sax.parse to parse untrusted XML data\n    is known to be vulnerable to XML attacks. Replace xml.sax.parse with its\n    defusedxml equivalent function.\n        Severity: Medium   Confidence: High\n        Location: ./examples/xml_sax.py:24\n        23    sax.parseString(xmlString, ExampleContentHandler())\n        24    sax.parse('notaxmlfilethatexists.xml', ExampleContentHandler)\n        25\n\nComplete Plugin Listing\n-----------------------\n\n.. toctree::\n   :maxdepth: 1\n   :glob:\n\n   *\n\n.. versionadded:: 0.17.0\n"
  },
  {
    "path": "doc/source/ci-cd/github-actions.rst",
    "content": "GitHub Actions Workflow for Bandit\n==================================\n\nThis document provides a minimal complete example workflow for \nsetting up a Code Scanning action using Bandit through GitHub \nActions. It leverages PyCQA's `bandit-action  \n<https://github.com/PyCQA/bandit-action>`_ for seamless \nintegration.  \n\nExample YAML Code for GitHub Actions Pipeline\n---------------------------------------------\n\nBelow is an example configuration for the GitHub Actions pipeline:\n\n.. code-block:: yaml\n\n    name: Bandit\n\n    on:\n        workflow_dispatch:\n\n    jobs:\n        analyze:\n            runs-on: ubuntu-latest\n            permissions:\n                # Required for all workflows\n                security-events: write\n                # Only required for workflows in private repositories\n                actions: read\n                contents: read\n            steps:\n                - name: Perform Bandit Analysis\n                  uses: PyCQA/bandit-action@v1\n\nInputs\n======\n\nBelow is a list of available inputs for the `bandit-action` and \ntheir descriptions:  \n\n.. list-table::\n   :header-rows: 1\n   :widths: 20 50 10 20\n\n   * - Name\n     - Description\n     - Required\n     - Default Value\n   * - ``configfile``\n     - Config file to use for selecting plugins and overriding defaults.\n     - False\n     - ``DEFAULT``\n   * - ``profile``\n     - Profile to use (defaults to executing all tests).\n     - False\n     - ``DEFAULT``\n   * - ``tests``\n     - Comma-separated list of test IDs to run.\n     - False\n     - ``DEFAULT``\n   * - ``skips``\n     - Comma-separated list of test IDs to skip.\n     - False\n     - ``DEFAULT``\n   * - ``severity``\n     - Report only issues of a given severity level or higher. Options include ``all``, ``high``, ``medium``, ``low``. \n       Note: ``all`` and ``low`` may produce similar results, but undefined rules will not be listed under ``low``.\n     - False\n     - ``DEFAULT``\n   * - ``confidence``\n     - Report only issues of a given confidence level or higher. Options include ``all``, ``high``, ``medium``, ``low``. \n       Note: ``all`` and ``low`` may produce similar results, but undefined rules will not be listed under ``low``.\n     - False\n     - ``DEFAULT``\n   * - ``exclude``\n     - Comma-separated list of paths (glob patterns supported) to exclude from the scan. These are in addition to excluded paths provided in the config file.\n     - False\n     - ``.svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg``\n   * - ``baseline``\n     - Path of a baseline report to compare against (only JSON-formatted files are accepted).\n     - False\n     - ``DEFAULT``\n   * - ``ini``\n     - Path to a ``.bandit`` file that supplies command-line arguments.\n     - False\n     - ``DEFAULT``\n   * - ``targets``\n     - Source file(s) or directory(s) to be tested.\n     - False\n     - ``.``"
  },
  {
    "path": "doc/source/ci-cd/index.rst",
    "content": ".. _ci-cd:\n\nContinuous Integration and Deployment (CI/CD)\n=============================================\n\nThis section provides documentation for setting up Continuous \nIntegration and Deployment (CI/CD) pipelines for automated \nsecurity scanning and quality assurance in this project.\n\nSupported CI/CD Platforms\n-------------------------  \n\nThe following CI/CD platforms are covered:\n\n- **GitHub Actions**: Example workflows for security scanning and quality checks.\n\nAvailable Documentation\n-----------------------\n\n.. toctree::\n   :maxdepth: 1\n\n   github-actions\n\nMore CI/CD platforms and configurations may be added over time. \nContributions and improvements to these configurations are \nwelcome.\n"
  },
  {
    "path": "doc/source/conf.py",
    "content": "# SPDX-License-Identifier: Apache-2.0\nfrom datetime import datetime\nimport os\nimport sys\n\nsys.path.insert(0, os.path.abspath(os.path.join(\"..\", \"..\")))\n# -- General configuration ----------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.doctest\",\n    \"sphinx.ext.coverage\",\n    \"sphinx.ext.viewcode\",\n    \"sphinx_copybutton\",\n]\n\n# autodoc generation is a bit aggressive and a nuisance when doing heavy\n# text edit cycles.\n# execute \"export SPHINX_DEBUG=1\" in your terminal to disable\n\n# The suffix of source filenames.\nsource_suffix = \".rst\"\n\n# The root toctree document.\nroot_doc = \"index\"\n\n# General information about the project.\nproject = \"Bandit\"\ncopyright = f\"{datetime.now():%Y}, Bandit Developers\"\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\nadd_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\nadd_module_names = True\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = \"sphinx\"\n\nmodindex_common_prefix = [\"bandit.\"]\n\n# -- Options for man page output --------------------------------------------\n\n# Grouping the document tree for man pages.\n# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual'\n\nman_pages = [\n    (\n        \"man/bandit\",\n        \"bandit\",\n        \"Python source code security analyzer\",\n        [\"PyCQA\"],\n        1,\n    )\n]\n\n# -- Options for HTML output --------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  Major themes that come with\n# Sphinx are currently 'default' and 'sphinxdoc'.\n# html_theme_path = [\".\"]\nhtml_theme = \"sphinx_rtd_theme\"\n# html_static_path = ['static']\nhtml_theme_options = {}\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = f\"{project}doc\"\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass\n# [howto/manual]).\nlatex_documents = [\n    (\n        \"index\",\n        f\"{project}.tex\",\n        f\"{project} Documentation\",\n        \"PyCQA\",\n        \"manual\",\n    ),\n]\n\n# Example configuration for intersphinx: refer to the Python standard library.\n# intersphinx_mapping = {'http://docs.python.org/': None}\n"
  },
  {
    "path": "doc/source/config.rst",
    "content": "Configuration\n=============\n\n---------------\nBandit Settings\n---------------\n\nProjects may include an INI file named `.bandit`, which specifies\ncommand line arguments that should be supplied for that project.\nIn addition or alternatively, you can use a YAML or TOML file, which\nhowever needs to be explicitly specified using the `-c` option.\nThe currently supported arguments are:\n\n``targets``\n  comma separated list of target dirs/files to run bandit on\n``exclude``\n  comma separated list of excluded paths -- *INI only*\n``exclude_dirs``\n  comma separated list of excluded paths (directories or files) -- *YAML and TOML only*\n``skips``\n  comma separated list of tests to skip\n``tests``\n  comma separated list of tests to run\n\nTo use this, put an INI file named `.bandit` in your project's directory.\nCommand line arguments must be in `[bandit]` section.\nFor example:\n\n.. code-block:: ini\n\n  # FILE: .bandit\n  [bandit]\n  exclude = tests,path/to/file\n  tests = B201,B301\n  skips = B101,B601\n\nAlternatively, put a YAML or TOML file anywhere, and use the `-c` option.\nFor example:\n\n.. code-block:: yaml\n\n  # FILE: bandit.yaml\n  exclude_dirs: ['tests', 'path/to/file']\n  tests: ['B201', 'B301']\n  skips: ['B101', 'B601']\n\n.. code-block:: toml\n\n  # FILE: pyproject.toml\n  [tool.bandit]\n  exclude_dirs = [\"tests\", \"path/to/file\"]\n  tests = [\"B201\", \"B301\"]\n  skips = [\"B101\", \"B601\"]\n\nThen run bandit like this:\n\n.. code-block:: console\n\n  bandit -c bandit.yaml -r .\n\n.. code-block:: console\n\n  bandit -c pyproject.toml -r .\n\nNote that Bandit will look for `.bandit` file only if it is invoked with `-r` option.\nIf you do not use `-r` or the INI file's name is not `.bandit`, you can specify\nthe file's path explicitly with `--ini` option, e.g.\n\n.. code-block:: console\n\n  bandit --ini tox.ini\n\nIf Bandit is used via `pre-commit`_ and a config file, you have to specify the config file\nand optional additional dependencies in the `pre-commit`_ configuration:\n\n.. code-block:: yaml\n\n    repos:\n    - repo: https://github.com/PyCQA/bandit\n      rev: '' # Update me!\n      hooks:\n      - id: bandit\n        args: [\"-c\", \"pyproject.toml\"]\n        additional_dependencies: [\"bandit[toml]\"]\n\nExclusions\n----------\n\nIn the event that a line of code triggers a Bandit issue, but that the line\nhas been reviewed and the issue is a false positive or acceptable for some\nother reason, the line can be marked with a ``# nosec`` and any results\nassociated with it will not be reported.\n\nFor example, although this line may cause Bandit to report a potential\nsecurity issue, it will not be reported:\n\n.. code-block:: python\n\n  self.process = subprocess.Popen('/bin/echo', shell=True)  # nosec\n\nBecause multiple issues can be reported for the same line, specific tests may\nbe provided to suppress those reports. This will cause other issues not\nincluded to be reported. This can be useful in preventing situations where a\nnosec comment is used, but a separate vulnerability may be added to the line\nlater causing the new vulnerability to be ignored.\n\nFor example, this will suppress the report of B602 and B607:\n\n.. code-block:: python\n\n  self.process = subprocess.Popen('/bin/ls *', shell=True)  # nosec B602, B607\n\nFull test names rather than the test ID may also be used.\n\nFor example, this will suppress the report of B101 and continue to report B506\nas an issue.\n\n.. code-block:: python\n\n  assert yaml.load(\"{}\") == []  # nosec assert_used\n\n-----------------\nScanning Behavior\n-----------------\n\nBandit is designed to be configurable and cover a wide range of needs, it may\nbe used as either a local developer utility or as part of a full CI/CD\npipeline. To provide for these various usage scenarios bandit can be configured\nvia a `YAML file`_. This file is completely optional and in many cases not\nneeded, it may be specified on the command line by using `-c`.\n\nA bandit configuration file may choose the specific test plugins to run and\noverride the default configurations of those tests. An example config might\nlook like the following:\n\n.. code-block:: yaml\n\n  ### profile may optionally select or skip tests\n\n  exclude_dirs: ['tests', 'path/to/file']\n\n  # (optional) list included tests here:\n  tests: ['B201', 'B301']\n\n  # (optional) list skipped tests here:\n  skips: ['B101', 'B601']\n\n  ### override settings - used to set settings for plugins to non-default values\n\n  any_other_function_with_shell_equals_true:\n    no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve,\n      os.execvp, os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe,\n      os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe, os.startfile]\n    shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4,\n      popen2.popen2, popen2.popen3, popen2.popen4, popen2.Popen3,\n      popen2.Popen4, commands.getoutput,  commands.getstatusoutput]\n    subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call,\n      subprocess.check_output]\n\nRun with:\n\n.. code-block:: console\n\n  bandit -c bandit.yaml -r .\n\nIf you require several sets of tests for specific tasks, then you should create\nseveral config files and pick from them using `-c`. If you only wish to control\nthe specific tests that are to be run (and not their parameters) then using\n`-s` or `-t` on the command line may be more appropriate.\n\nAlso, you can configure bandit via a `pyproject.toml file`_. In this case you\nwould explicitly specify the path to configuration via `-c`, too. For example:\n\n.. code-block:: toml\n\n  [tool.bandit]\n  exclude_dirs = [\"tests\", \"path/to/file\"]\n  tests = [\"B201\", \"B301\"]\n  skips = [\"B101\", \"B601\"]\n\n  [tool.bandit.any_other_function_with_shell_equals_true]\n  no_shell = [\n    \"os.execl\",\n    \"os.execle\",\n    \"os.execlp\",\n    \"os.execlpe\",\n    \"os.execv\",\n    \"os.execve\",\n    \"os.execvp\",\n    \"os.execvpe\",\n    \"os.spawnl\",\n    \"os.spawnle\",\n    \"os.spawnlp\",\n    \"os.spawnlpe\",\n    \"os.spawnv\",\n    \"os.spawnve\",\n    \"os.spawnvp\",\n    \"os.spawnvpe\",\n    \"os.startfile\"\n  ]\n  shell = [\n    \"os.system\",\n    \"os.popen\",\n    \"os.popen2\",\n    \"os.popen3\",\n    \"os.popen4\",\n    \"popen2.popen2\",\n    \"popen2.popen3\",\n    \"popen2.popen4\",\n    \"popen2.Popen3\",\n    \"popen2.Popen4\",\n    \"commands.getoutput\",\n    \"commands.getstatusoutput\"\n  ]\n  subprocess = [\n    \"subprocess.Popen\",\n    \"subprocess.call\",\n    \"subprocess.check_call\",\n    \"subprocess.check_output\"\n  ]\n\nRun with:\n\n.. code-block:: console\n\n  bandit -c pyproject.toml -r .\n\n.. _YAML file: https://yaml.org/\n.. _pyproject.toml file: https://www.python.org/dev/peps/pep-0518/\n\nSkipping Tests\n--------------\n\nThe bandit config may contain optional lists of test IDs to either include\n(`tests`) or exclude (`skips`). These lists are equivalent to using `-t` and\n`-s` on the command line. If only `tests` is given then bandit will include\nonly those tests, effectively excluding all other tests. If only `skips`\nis given then bandit will include all tests not in the skips list. If both are\ngiven then bandit will include only tests in `tests` and then remove `skips`\nfrom that set. It is an error to include the same test ID in both `tests` and\n`skips`.\n\nNote that command line options `-t`/`-s` can still be used in conjunction with\n`tests` and `skips` given in a config. The result is to concatenate `-t` with\n`tests` and likewise for `-s` and `skips` before working out the tests to run.\n\nSuppressing Individual Lines\n----------------------------\n\nIf you have lines in your code triggering vulnerability errors and you are\ncertain that this is acceptable, they can be individually silenced by appending\n``# nosec`` to the line:\n\n.. code-block:: python\n\n    # The following hash is not used in any security context. It is only used\n    # to generate unique values, collisions are acceptable and \"data\" is not\n    # coming from user-generated input\n    the_hash = md5(data).hexdigest()  # nosec\n\nIn such cases, it is good practice to add a comment explaining *why* a given\nline was excluded from security checks.\n\nGenerating a Config\n-------------------\n\nBandit ships the tool `bandit-config-generator` designed to take the leg work\nout of configuration. This tool can generate a configuration file\nautomatically. The generated configuration will include default config blocks\nfor all detected test and blacklist plugins. This data can then be deleted or\nedited as needed to produce a minimal config as desired. The config generator\nsupports `-t` and `-s` command line options to specify a list of test IDs that\nshould be included or excluded respectively. If no options are given then the\ngenerated config will not include `tests` or `skips` sections (but will provide\na complete list of all test IDs for reference when editing).\n\nConfiguring Test Plugins\n------------------------\n\nBandit's configuration file is written in `YAML`_ and options\nfor each plugin test are provided under a section named to match the test\nmethod. For example, given a test plugin called 'try_except_pass' its\nconfiguration section might look like the following:\n\n.. code-block:: yaml\n\n    try_except_pass:\n      check_typed_exception: True\n\nThe specific content of the configuration block is determined by the plugin\ntest itself. See the `plugin test list`_ for complete information on\nconfiguring each one.\n\n\n.. _YAML: https://yaml.org/\n.. _plugin test list: plugins/index.html\n.. _pre-commit: https://pre-commit.com/\n"
  },
  {
    "path": "doc/source/faq.rst",
    "content": "Frequently Asked Questions\n==========================\n\nUnder Which Version of Python Should I Install Bandit?\n------------------------------------------------------\n\nThe answer to this question depends on the project(s) you will be running\nBandit against. If your project is only compatible with Python 3.9, you\nshould install Bandit to run under Python 3.9. If your project is only\ncompatible with Python 3.10, then use 3.10 respectively. If your project\nsupports both, you *could* run Bandit with both versions but you don't have to.\n\nBandit uses the `ast` module from Python's standard library in order to\nanalyze your Python code. The `ast` module is only able to parse Python code\nthat is valid in the version of the interpreter from which it is imported. In\nother words, if you try to use Python 2.7's `ast` module to parse code written\nfor 3.5 that uses, for example, `yield from` with asyncio, then you'll have\nsyntax errors that will prevent Bandit from working properly. Alternatively,\nif you are relying on 2.7's octal notation of `0777` then you'll have a syntax\nerror if you run Bandit on 3.x.\n"
  },
  {
    "path": "doc/source/formatters/csv.rst",
    "content": "---\ncsv\n---\n\n.. automodule:: bandit.formatters.csv\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/custom.rst",
    "content": "------\ncustom\n------\n\n.. automodule:: bandit.formatters.custom\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/html.rst",
    "content": "----\nhtml\n----\n\n.. automodule:: bandit.formatters.html\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/index.rst",
    "content": "Report Formatters\n=================\n\nBandit supports many different formatters to output various security issues in\npython code. These formatters are created as plugins and new ones can be\ncreated to extend the functionality offered by bandit today.\n\nExample Formatter\n-----------------\n\n.. code-block:: python\n\n    def report(manager, fileobj, sev_level, conf_level, lines=-1):\n        result = bson.dumps(issues)\n        with fileobj:\n            fileobj.write(result)\n\nTo register your plugin, you have two options:\n\n1. If you're using setuptools directly, add something like the following to\n   your `setup` call::\n\n        # If you have an imaginary bson formatter in the bandit_bson module\n        # and a function called `formatter`.\n        entry_points={'bandit.formatters': ['bson = bandit_bson:formatter']}\n\n2. If you're using pbr, add something like the following to your `setup.cfg`\n   file::\n\n        [entry_points]\n        bandit.formatters =\n            bson = bandit_bson:formatter\n\n\nComplete Formatter Listing\n----------------------------\n\n.. toctree::\n   :maxdepth: 1\n   :glob:\n\n   *\n"
  },
  {
    "path": "doc/source/formatters/json.rst",
    "content": "----\njson\n----\n\n.. automodule:: bandit.formatters.json\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/sarif.rst",
    "content": "-----\nsarif\n-----\n\n.. automodule:: bandit.formatters.sarif\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/screen.rst",
    "content": "------\nscreen\n------\n\n.. automodule:: bandit.formatters.screen\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/text.rst",
    "content": "----\ntext\n----\n\n.. automodule:: bandit.formatters.text\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/xml.rst",
    "content": "---\nxml\n---\n\n.. automodule:: bandit.formatters.xml\n   :no-index:\n"
  },
  {
    "path": "doc/source/formatters/yaml.rst",
    "content": "----\nyaml\n----\n\n.. automodule:: bandit.formatters.yaml\n   :no-index:\n"
  },
  {
    "path": "doc/source/index.rst",
    "content": "Welcome to Bandit\n=================\n\nBandit is a tool designed to find common security issues in Python code. To do\nthis, Bandit processes each file, builds an AST from it, and runs appropriate\nplugins against the AST nodes.  Once Bandit has finished scanning all the files,\nit generates a report.\n\nUsing and Extending Bandit\n==========================\n.. toctree::\n   :maxdepth: 1\n\n   start\n   config\n   integrations\n   plugins/index\n   blacklists/index\n   formatters/index\n   ci-cd/index\n   faq\n\nContributing\n============\n\n* `Source code`_\n* `Issue tracker`_\n* Join us on `Discord`_\n\n.. _`Source code`: https://github.com/PyCQA/bandit\n.. _`Issue tracker`: https://github.com/PyCQA/bandit/issues\n.. _`Discord`: https://discord.gg/qYxpadCgkx\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n\nLicense\n=======\n\nThe ``bandit`` library is provided under the terms and conditions of the\n`Apache License 2.0 <https://www.apache.org/licenses/LICENSE-2.0.txt>`_\n"
  },
  {
    "path": "doc/source/integrations.rst",
    "content": "Integrations\n============\n\nBandit can be integrated into a wide variety of developer tools, editors,\nCI/CD systems, and code quality pipelines. This page outlines popular\nintegrations to help you seamlessly incorporate Bandit into your development\nworkflow.\n\nIDE Integrations\n----------------\n\n.. list-table::\n   :widths: 30 70\n\n   * - Visual Studio Code\n     - `Bandit by PyCQA <https://marketplace.visualstudio.com/items?itemName=pycqa.bandit-pycqa>`_\n   * - Sublime Text\n     - `SublimeLinter-bandit <https://github.com/SublimeLinter/SublimeLinter-bandit>`_\n   * - Vim/Neovim\n     - `Asynchronous Lint Engine <https://github.com/dense-analysis/ale>`_\n   * - Emacs\n     - `flycheck-pycheckers <https://github.com/msherry/flycheck-pycheckers>`_\n\nCI/CD Integrations\n------------------\n\n.. list-table::\n   :widths: 30 70\n\n   * - GitHub Action\n     - `Bandit by PyCQA <https://github.com/marketplace/actions/bandit-by-pycqa>`_\n   * - Hudson/Jenkins\n     - `Bandit Plugin <https://github.com/mewz/bandit-plugin->`_\n\nLinters\n-------\n\n.. list-table::\n   :widths: 30 70\n\n   * - Ruff\n     - `flake8-bandit (S) <https://docs.astral.sh/ruff/rules/#flake8-bandit-s>`_\n   * - Flake8\n     - `flake8-bandit <https://github.com/tylerwince/flake8-bandit>`_\n\nPackages\n--------\n\n.. list-table::\n   :widths: 30 70\n\n   * - Ubuntu\n     - `bandit <https://packages.ubuntu.com/search?keywords=bandit&searchon=names&section=all>`_\n   * - Homebrew\n     - `bandit <https://formulae.brew.sh/formula/bandit>`_\n   * - FreeBSD\n     - `py-bandit <https://www.freshports.org/devel/py-bandit/>`_\n\n\n🙌 Contributions Welcome\n\nIf you’ve integrated Bandit into another platform or tool, feel free to open\na PR and update this page!\n"
  },
  {
    "path": "doc/source/man/bandit.rst",
    "content": "======\nbandit\n======\n\nSYNOPSIS\n========\n\nbandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE]\n            [-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i]\n            [-f {csv,custom,html,json,screen,txt,xml,yaml}]\n            [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] [-q]\n            [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE]\n            [--ini INI_PATH] [--exit-zero] [--version]\n            [targets [targets ...]]\n\nDESCRIPTION\n===========\n\n``bandit`` is a tool designed to find common security issues in Python code. To\ndo this Bandit processes each file, builds an AST from it, and runs appropriate\nplugins against the AST nodes.  Once Bandit has finished scanning all the files\nit generates a report.\n\nOPTIONS\n=======\n\n  -h, --help            show this help message and exit\n  -r, --recursive       find and process files in subdirectories\n  -a {file,vuln}, --aggregate {file,vuln}\n                        aggregate output by vulnerability (default) or by\n                        filename\n  -n CONTEXT_LINES, --number CONTEXT_LINES\n                        maximum number of code lines to output for each issue\n  -c CONFIG_FILE, --configfile CONFIG_FILE\n                        optional config file to use for selecting plugins and\n                        overriding defaults\n  -p PROFILE, --profile PROFILE\n                        profile to use (defaults to executing all tests)\n  -t TESTS, --tests TESTS\n                        comma-separated list of test IDs to run\n  -s SKIPS, --skip SKIPS\n                        comma-separated list of test IDs to skip\n  -l, --level           report only issues of a given severity level or higher\n                        (-l for LOW, -ll for MEDIUM, -lll for HIGH)\n  -l, --severity-level={all,high,medium,low}\n                        report only issues of a given severity level or higher.\n                        \"all\" and \"low\" are likely to produce the same results, but it\n                        is possible for rules to be undefined which will not be listed in \"low\".\n  -i, --confidence      report only issues of a given confidence level or\n                        higher (-i for LOW, -ii for MEDIUM, -iii for HIGH)\n  -l, --confidence-level={all,high,medium,low}\n                        report only issues of a given confidence level or higher.\n                        \"all\" and \"low\" are likely to produce the same results, but it\n                        is possible for rules to be undefined which will not be listed in \"low\".\n  -f {csv,custom,html,json,sarif,screen,txt,xml,yaml}, --format {csv,custom,html,json,sarif,screen,txt,xml,yaml}\n                        specify output format\n  --msg-template MSG_TEMPLATE\n                        specify output message template (only usable with\n                        --format custom), see CUSTOM FORMAT section for list\n                        of available values\n  -o OUTPUT_FILE, --output OUTPUT_FILE\n                        write report to filename\n  -v, --verbose         output extra information like excluded and included files\n  -d, --debug           turn on debug mode\n  -q, --quiet, --silent\n                        only show output in the case of an error\n  --ignore-nosec        do not skip lines with # nosec comments\n  -x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS\n                        comma-separated list of paths (glob patterns\n                        supported) to exclude from scan (note that these are\n                        in addition to the excluded paths provided in the\n                        config file) (default:\n                        .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg)\n  -b BASELINE, --baseline BASELINE\n                        path of a baseline report to compare against (only\n                        JSON-formatted files are accepted)\n  --ini INI_PATH        path to a .bandit file that supplies command line arguments\n  --exit-zero           exit with 0, even with results found\n  --version             show program's version number and exit\n\nCUSTOM FORMATTING\n-----------------\n\nAvailable tags:\n\n    {abspath}, {relpath}, {line},  {test_id},\n    {severity}, {msg}, {confidence}, {range}\n\nExample usage:\n\n    Default template:\n    bandit -r examples/ --format custom --msg-template \\\n    \"{abspath}:{line}: {test_id}[bandit]: {severity}: {msg}\"\n\n    Provides same output as:\n    bandit -r examples/ --format custom\n\n    Tags can also be formatted in python string.format() style:\n    bandit -r examples/ --format custom --msg-template \\\n    \"{relpath:20.20s}: {line:03}: {test_id:^8}: DEFECT: {msg:>20}\"\n\n    See python documentation for more information about formatting style:\n    https://docs.python.org/3/library/string.html\n\nFILES\n=====\n\n.bandit\n  file that supplies command line arguments\n\n/etc/bandit/bandit.yaml\n  legacy bandit configuration file\n\nEXAMPLES\n========\n\nExample usage across a code tree::\n\n    bandit -r ~/your-repos/project\n\nExample usage across the ``examples/`` directory, showing three lines of\ncontext and only reporting on the high-severity issues::\n\n    bandit examples/*.py -n 3 --severity-level=high\n\nBandit can be run with profiles.  To run Bandit against the examples directory\nusing only the plugins listed in the ShellInjection profile::\n\n    bandit examples/*.py -p ShellInjection\n\nBandit also supports passing lines of code to scan using standard input. To\nrun Bandit with standard input::\n\n    cat examples/imports.py | bandit -\n\nSEE ALSO\n========\n\npylint(1)\n"
  },
  {
    "path": "doc/source/plugins/b101_assert_used.rst",
    "content": "-----------------\nB101: assert_used\n-----------------\n\n.. automodule:: bandit.plugins.asserts\n   :no-index:"
  },
  {
    "path": "doc/source/plugins/b102_exec_used.rst",
    "content": "---------------\nB102: exec_used\n---------------\n\n.. automodule:: bandit.plugins.exec\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b103_set_bad_file_permissions.rst",
    "content": "------------------------------\nB103: set_bad_file_permissions\n------------------------------\n\n.. automodule:: bandit.plugins.general_bad_file_permissions\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b104_hardcoded_bind_all_interfaces.rst",
    "content": "-----------------------------------\nB104: hardcoded_bind_all_interfaces\n-----------------------------------\n\n.. automodule:: bandit.plugins.general_bind_all_interfaces\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b105_hardcoded_password_string.rst",
    "content": "-------------------------------\nB105: hardcoded_password_string\n-------------------------------\n\n.. currentmodule:: bandit.plugins.general_hardcoded_password\n\n.. autofunction:: hardcoded_password_string\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b106_hardcoded_password_funcarg.rst",
    "content": "--------------------------------\nB106: hardcoded_password_funcarg\n--------------------------------\n\n.. currentmodule:: bandit.plugins.general_hardcoded_password\n\n.. autofunction:: hardcoded_password_funcarg\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b107_hardcoded_password_default.rst",
    "content": "--------------------------------\nB107: hardcoded_password_default\n--------------------------------\n\n.. currentmodule:: bandit.plugins.general_hardcoded_password\n\n.. autofunction:: hardcoded_password_default\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b108_hardcoded_tmp_directory.rst",
    "content": "-----------------------------\nB108: hardcoded_tmp_directory\n-----------------------------\n\n.. automodule:: bandit.plugins.general_hardcoded_tmp\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b109_password_config_option_not_marked_secret.rst",
    "content": "----------------------------------------------\nB109: password_config_option_not_marked_secret\n----------------------------------------------\n\nThis plugin has been removed.\n\nB109: Test for a password based config option not marked secret\n\nPasswords are sensitive and must be protected appropriately. In OpenStack\nOslo there is an option to mark options \"secret\" which will ensure that they\nare not logged. This plugin detects usages of oslo configuration functions\nthat appear to deal with strings ending in 'password' and flag usages where\nthey have not been marked secret.\n\nIf such a value is found a MEDIUM severity error is generated. If 'False' or\n'None' are explicitly set, Bandit will return a MEDIUM confidence issue. If\nBandit can't determine the value of secret it will return a LOW confidence\nissue.\n\n\n**Config Options:**\n\n.. code-block:: yaml\n\n    password_config_option_not_marked_secret:\n        function_names:\n            - oslo.config.cfg.StrOpt\n            - oslo_config.cfg.StrOpt\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: [password_config_option_not_marked_secret] oslo config option\n    possibly not marked secret=True identified.\n       Severity: Medium   Confidence: Low\n       Location: examples/secret-config-option.py:12\n    11                  help=\"User's password\"),\n    12       cfg.StrOpt('nova_password',\n    13                  secret=secret,\n    14                  help=\"Nova user password\"),\n    15   ]\n\n    >> Issue: [password_config_option_not_marked_secret] oslo config option not\n    marked secret=True identified, security issue.\n       Severity: Medium   Confidence: Medium\n       Location: examples/secret-config-option.py:21\n    20                  help=\"LDAP ubind ser name\"),\n    21       cfg.StrOpt('ldap_password',\n    22                  help=\"LDAP bind user password\"),\n    23       cfg.StrOpt('ldap_password_attribute',\n\n.. seealso::\n\n - https://security.openstack.org/guidelines/dg_protect-sensitive-data-in-files.html\n\n.. versionadded:: 0.10.0\n\n.. deprecated:: 1.5.0\n   This plugin was removed\n"
  },
  {
    "path": "doc/source/plugins/b110_try_except_pass.rst",
    "content": "---------------------\nB110: try_except_pass\n---------------------\n\n.. automodule:: bandit.plugins.try_except_pass\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b111_execute_with_run_as_root_equals_true.rst",
    "content": "------------------------------------------\nB111: execute_with_run_as_root_equals_true\n------------------------------------------\n\nThis plugin has been removed.\n\nB111: Test for the use of rootwrap running as root\n\nRunning commands as root dramatically increase their potential risk. Running\ncommands with restricted user privileges provides defense in depth against\ncommand injection attacks, or developer and configuration error. This plugin\ntest checks for specific methods being called with a keyword parameter\n`run_as_root` set to True, a common OpenStack idiom.\n\n\n**Config Options:**\n\nThis test plugin takes a similarly named configuration block,\n`execute_with_run_as_root_equals_true`, providing a list, `function_names`, of\nfunction names. A call to any of these named functions will be checked for a\n`run_as_root` keyword parameter, and if True, will report a Low severity\nissue.\n\n.. code-block:: yaml\n\n    execute_with_run_as_root_equals_true:\n        function_names:\n            - ceilometer.utils.execute\n            - cinder.utils.execute\n            - neutron.agent.linux.utils.execute\n            - nova.utils.execute\n            - nova.utils.trycmd\n\n:Example:\n\n.. code-block:: none\n\n    >> Issue: Execute with run_as_root=True identified, possible security\n       issue.\n       Severity: Low   Confidence: Medium\n       Location: ./examples/exec-as-root.py:26\n    25  nova_utils.trycmd('gcc --version')\n    26  nova_utils.trycmd('gcc --version', run_as_root=True)\n    27\n\n.. seealso::\n\n - https://security.openstack.org/guidelines/dg_rootwrap-recommendations-and-plans.html\n - https://security.openstack.org/guidelines/dg_use-oslo-rootwrap-securely.html\n\n.. versionadded:: 0.10.0\n\n.. deprecated:: 1.5.0\n   This plugin was removed\n"
  },
  {
    "path": "doc/source/plugins/b112_try_except_continue.rst",
    "content": "-------------------------\nB112: try_except_continue\n-------------------------\n\n.. automodule:: bandit.plugins.try_except_continue\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b113_request_without_timeout.rst",
    "content": "-----------------------------\nB113: request_without_timeout\n-----------------------------\n\n.. automodule:: bandit.plugins.request_without_timeout\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b201_flask_debug_true.rst",
    "content": "----------------------\nB201: flask_debug_true\n----------------------\n\n.. automodule:: bandit.plugins.app_debug\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b202_tarfile_unsafe_members.rst",
    "content": "----------------------------\nB202: tarfile_unsafe_members\n----------------------------\n\n.. automodule:: bandit.plugins.tarfile_unsafe_members\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b324_hashlib.rst",
    "content": "-------------\nB324: hashlib\n-------------\n\n.. automodule:: bandit.plugins.hashlib_insecure_functions\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b501_request_with_no_cert_validation.rst",
    "content": "-------------------------------------\nB501: request_with_no_cert_validation\n-------------------------------------\n\n.. automodule:: bandit.plugins.crypto_request_no_cert_validation\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b502_ssl_with_bad_version.rst",
    "content": "--------------------------\nB502: ssl_with_bad_version\n--------------------------\n\n.. currentmodule:: bandit.plugins.insecure_ssl_tls\n\n.. autofunction:: ssl_with_bad_version\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b503_ssl_with_bad_defaults.rst",
    "content": "---------------------------\nB503: ssl_with_bad_defaults\n---------------------------\n\n.. currentmodule:: bandit.plugins.insecure_ssl_tls\n\n.. autofunction:: ssl_with_bad_defaults\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b504_ssl_with_no_version.rst",
    "content": "-------------------------\nB504: ssl_with_no_version\n-------------------------\n\n.. currentmodule:: bandit.plugins.insecure_ssl_tls\n\n.. autofunction:: ssl_with_no_version\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b505_weak_cryptographic_key.rst",
    "content": "----------------------------\nB505: weak_cryptographic_key\n----------------------------\n\n.. automodule:: bandit.plugins.weak_cryptographic_key\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b506_yaml_load.rst",
    "content": "---------------\nB506: yaml_load\n---------------\n\n.. automodule:: bandit.plugins.yaml_load\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b507_ssh_no_host_key_verification.rst",
    "content": "----------------------------------\nB507: ssh_no_host_key_verification\n----------------------------------\n\n.. automodule:: bandit.plugins.ssh_no_host_key_verification\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b508_snmp_insecure_version.rst",
    "content": "---------------------------\nB508: snmp_insecure_version\n---------------------------\n\n.. currentmodule:: bandit.plugins.snmp_security_check\n\n.. autofunction:: snmp_insecure_version_check\n   :noindex:"
  },
  {
    "path": "doc/source/plugins/b509_snmp_weak_cryptography.rst",
    "content": "----------------------------\nB509: snmp_weak_cryptography\n----------------------------\n\n.. currentmodule:: bandit.plugins.snmp_security_check\n\n.. autofunction:: snmp_crypto_check\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b601_paramiko_calls.rst",
    "content": "--------------------\nB601: paramiko_calls\n--------------------\n\n.. automodule:: bandit.plugins.injection_paramiko\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b602_subprocess_popen_with_shell_equals_true.rst",
    "content": "---------------------------------------------\nB602: subprocess_popen_with_shell_equals_true\n---------------------------------------------\n\n.. currentmodule:: bandit.plugins.injection_shell\n\n.. autofunction:: subprocess_popen_with_shell_equals_true\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b603_subprocess_without_shell_equals_true.rst",
    "content": "------------------------------------------\nB603: subprocess_without_shell_equals_true\n------------------------------------------\n\n.. currentmodule:: bandit.plugins.injection_shell\n\n.. autofunction:: subprocess_without_shell_equals_true\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b604_any_other_function_with_shell_equals_true.rst",
    "content": "-----------------------------------------------\nB604: any_other_function_with_shell_equals_true\n-----------------------------------------------\n\n.. currentmodule:: bandit.plugins.injection_shell\n\n.. autofunction:: any_other_function_with_shell_equals_true\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b605_start_process_with_a_shell.rst",
    "content": "--------------------------------\nB605: start_process_with_a_shell\n--------------------------------\n\n.. currentmodule:: bandit.plugins.injection_shell\n\n.. autofunction:: start_process_with_a_shell\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b606_start_process_with_no_shell.rst",
    "content": "---------------------------------\nB606: start_process_with_no_shell\n---------------------------------\n\n.. currentmodule:: bandit.plugins.injection_shell\n\n.. autofunction:: start_process_with_no_shell\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b607_start_process_with_partial_path.rst",
    "content": "-------------------------------------\nB607: start_process_with_partial_path\n-------------------------------------\n\n.. currentmodule:: bandit.plugins.injection_shell\n\n.. autofunction:: start_process_with_partial_path\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b608_hardcoded_sql_expressions.rst",
    "content": "-------------------------------\nB608: hardcoded_sql_expressions\n-------------------------------\n\n.. automodule:: bandit.plugins.injection_sql\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b609_linux_commands_wildcard_injection.rst",
    "content": "---------------------------------------\nB609: linux_commands_wildcard_injection\n---------------------------------------\n\n.. automodule:: bandit.plugins.injection_wildcard\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b610_django_extra_used.rst",
    "content": "-----------------------\nB610: django_extra_used\n-----------------------\n\n.. currentmodule:: bandit.plugins.django_sql_injection\n\n.. autofunction:: django_extra_used\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b611_django_rawsql_used.rst",
    "content": "------------------------\nB611: django_rawsql_used\n------------------------\n\n.. currentmodule:: bandit.plugins.django_sql_injection\n\n.. autofunction:: django_rawsql_used\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b612_logging_config_insecure_listen.rst",
    "content": "------------------------------------\nB612: logging_config_insecure_listen\n------------------------------------\n\n.. automodule:: bandit.plugins.logging_config_insecure_listen\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b613_trojansource.rst",
    "content": "------------------\nB613: trojansource\n------------------\n\n.. automodule:: bandit.plugins.trojansource\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b614_pytorch_load.rst",
    "content": "------------------\nB614: pytorch_load\n------------------\n\n.. automodule:: bandit.plugins.pytorch_load\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b615_huggingface_unsafe_download.rst",
    "content": "---------------------------------\nB615: huggingface_unsafe_download\n---------------------------------\n\n.. automodule:: bandit.plugins.huggingface_unsafe_download\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b701_jinja2_autoescape_false.rst",
    "content": "-----------------------------\nB701: jinja2_autoescape_false\n-----------------------------\n\n.. automodule:: bandit.plugins.jinja2_templates\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b702_use_of_mako_templates.rst",
    "content": "---------------------------\nB702: use_of_mako_templates\n---------------------------\n\n.. automodule:: bandit.plugins.mako_templates\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/b703_django_mark_safe.rst",
    "content": "----------------------\nB703: django_mark_safe\n----------------------\n\n.. currentmodule:: bandit.plugins.django_xss\n\n.. autofunction:: django_mark_safe\n   :noindex:\n"
  },
  {
    "path": "doc/source/plugins/b704_markupsafe_markup_xss.rst",
    "content": "---------------------------\nB704: markupsafe_markup_xss\n---------------------------\n\n.. automodule:: bandit.plugins.markupsafe_markup_xss\n   :no-index:\n"
  },
  {
    "path": "doc/source/plugins/index.rst",
    "content": "Test Plugins\n============\n\nBandit supports many different tests to detect various security issues in\npython code. These tests are created as plugins and new ones can be created to\nextend the functionality offered by bandit today.\n\nWriting Tests\n-------------\nTo write a test:\n - Identify a vulnerability to build a test for, and create a new file in\n   examples/ that contains one or more cases of that vulnerability.\n - Create a new Python source file to contain your test, you can reference\n   existing tests for examples.\n - Consider the vulnerability you're testing for, mark the function with one\n   or more of the appropriate decorators:\n\n  - @checks('Call')\n  - @checks('Import', 'ImportFrom')\n  - @checks('Str')\n\n - Register your plugin using the `bandit.plugins` entry point, see example.\n - The function that you create should take a parameter \"context\" which is\n   an instance of the context class you can query for information about the\n   current element being examined.  You can also get the raw AST node for\n   more advanced use cases.  Please see the `context.py` file for more.\n - Extend your Bandit configuration file as needed to support your new test.\n - Execute Bandit against the test file you defined in `examples/` and ensure\n   that it detects the vulnerability.  Consider variations on how this\n   vulnerability might present itself and extend the example file and the test\n   function accordingly.\n\nConfig Generation\n-----------------\nIn Bandit 1.0+ config files are optional. Plugins that need config settings are\nrequired to implement a module global `gen_config` function. This function is\ncalled with a single parameter, the test plugin name. It should return a\ndictionary with keys being the config option names and values being the default\nsettings for each option. An example `gen_config` might look like the following:\n\n.. code-block:: python\n\n    def gen_config(name):\n        if name == 'try_except_continue':\n            return {'check_typed_exception': False}\n\n\nWhen no config file is specified, or when the chosen file has no section\npertaining to a given plugin, `gen_config` will be called to provide defaults.\n\nThe config file generation tool `bandit-config-generator` will also call\n`gen_config` on all discovered plugins to produce template config blocks. If\nthe defaults are acceptable then these blocks may be deleted to create a\nminimal configuration, or otherwise edited as needed. The above example would\nproduce the following config snippet.\n\n.. code-block:: yaml\n\n    try_except_continue: {check_typed_exception: false}\n\n\nExample Test Plugin\n-------------------\n\n.. code-block:: python\n\n    @bandit.checks('Call')\n    def prohibit_unsafe_deserialization(context):\n        if 'unsafe_load' in context.call_function_name_qual:\n            return bandit.Issue(\n                severity=bandit.HIGH,\n                confidence=bandit.HIGH,\n                text=\"Unsafe deserialization detected.\"\n            )\n\nTo register your plugin, you have two options:\n\n1. If you're using setuptools directly, add something like the following to\n   your `setup` call::\n\n        # If you have an imaginary bson formatter in the bandit_bson module\n        # and a function called `formatter`.\n        entry_points={'bandit.formatters': ['bson = bandit_bson:formatter']}\n        # Or a check for using mako templates in bandit_mako that\n        entry_points={'bandit.plugins': ['mako = bandit_mako']}\n\n2. If you're using pbr, add something like the following to your `setup.cfg`\n   file::\n\n        [entry_points]\n        bandit.formatters =\n            bson = bandit_bson:formatter\n        bandit.plugins =\n            mako = bandit_mako\n\n\nPlugin ID Groupings\n-------------------\n\n=======  ===========\nID       Description\n=======  ===========\nB1xx     misc tests\nB2xx     application/framework misconfiguration\nB3xx     blacklists (calls)\nB4xx     blacklists (imports)\nB5xx     cryptography\nB6xx     injection\nB7xx     XSS\n=======  ===========\n\n\nComplete Test Plugin Listing\n----------------------------\n\n.. toctree::\n   :maxdepth: 1\n   :glob:\n\n   *\n"
  },
  {
    "path": "doc/source/start.rst",
    "content": "Getting Started\n===============\n\nInstallation\n------------\n\nBandit is distributed on PyPI. The best way to install it is with pip.\n\nCreate a virtual environment and activate it using `virtualenv` (optional):\n\n.. code-block:: console\n\n    virtualenv bandit-env\n    source bandit-env/bin/activate\n\nAlternatively, use `venv` instead of `virtualenv` (optional):\n\n.. code-block:: console\n\n    python3 -m venv bandit-env\n    source bandit-env/bin/activate\n\nInstall Bandit:\n\n.. code-block:: console\n\n    pip install bandit\n\nIf you want to include TOML support, install it with the `toml` extras:\n\n.. code-block:: console\n\n    pip install bandit[toml]\n\nIf you want to use the bandit-baseline CLI, install it with the `baseline`\nextras:\n\n.. code-block:: console\n\n    pip install bandit[baseline]\n\nIf you want to include SARIF output formatter support, install it with the\n`sarif` extras:\n\n.. code-block:: console\n\n    pip install bandit[sarif]\n\nRun Bandit:\n\n.. code-block:: console\n\n    bandit -r path/to/your/code\n\nBandit can also be installed from source. To do so, either clone the\nrepository or download the source tarball from PyPI, then install it:\n\n.. code-block:: console\n\n    python setup.py install\n\nAlternatively, let pip do the downloading for you, like this:\n\n.. code-block:: console\n\n    pip install git+https://github.com/PyCQA/bandit#egg=bandit\n\nUsage\n-----\n\nExample usage across a code tree:\n\n.. code-block:: console\n\n    bandit -r ~/your_repos/project\n\nTwo examples of usage across the ``examples/`` directory, showing three lines of\ncontext and only reporting on the high-severity issues:\n\n.. code-block:: console\n\n    bandit examples/*.py -n 3 --severity-level=high\n\n.. code-block:: console\n\n    bandit examples/*.py -n 3 -lll\n\nBandit can be run with profiles. To run Bandit against the examples directory\nusing only the plugins listed in the ``ShellInjection`` profile:\n\n.. code-block:: console\n\n    bandit examples/*.py -p ShellInjection\n\nBandit also supports passing lines of code to scan using standard input. To\nrun Bandit with standard input:\n\n.. code-block:: console\n\n    cat examples/imports.py | bandit -\n\nFor more usage information:\n\n.. code-block:: console\n\n    bandit -h\n\nBaseline\n--------\n\nBandit allows specifying the path of a baseline report to compare against using the base line argument (i.e. ``-b BASELINE`` or ``--baseline BASELINE``).\n\n.. code-block:: console\n\n   bandit -b BASELINE\n\nThis is useful for ignoring known vulnerabilities that you believe are non-issues (e.g. a cleartext password in a unit test). To generate a baseline report simply run Bandit with the output format set to ``json`` (only JSON-formatted files are accepted as a baseline) and output file path specified:\n\n.. code-block:: console\n\n    bandit -f json -o PATH_TO_OUTPUT_FILE\n\nVersion control integration\n---------------------------\n\nUse `pre-commit`_. Once you `have it installed`_, add this to the\n``.pre-commit-config.yaml`` in your repository\n(be sure to update `rev` to point to a `real git tag/revision`_!):\n\n.. code-block:: yaml\n\n    repos:\n    - repo: https://github.com/PyCQA/bandit\n      rev: '' # Update me!\n      hooks:\n      - id: bandit\n\nThen run ``pre-commit install`` and you're ready to go.\n\n.. _pre-commit: https://pre-commit.com/\n.. _have it installed: https://pre-commit.com/#install\n.. _`real git tag/revision`: https://github.com/PyCQA/bandit/releases\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM python:3.12-alpine\n\n# Install Git (required for pbr versioning)\nRUN apk add --no-cache git\n\n# Copy the source code into the container\nCOPY . /bandit\n\n# Set the working directory\nWORKDIR /bandit\n\n# Install Bandit from the source code using pip\nRUN pip install .\n\n# Define entrypoint and default command\nENTRYPOINT [\"bandit\"]\n"
  },
  {
    "path": "examples/__init__.py",
    "content": ""
  },
  {
    "path": "examples/assert.py",
    "content": "assert True\n"
  },
  {
    "path": "examples/binding.py",
    "content": "import socket\n\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\ns.bind(('0.0.0.0', 31137))\ns.bind(('192.168.0.1', 8080))\n"
  },
  {
    "path": "examples/cipher-modes.py",
    "content": "from cryptography.hazmat.primitives.ciphers.modes import CBC\nfrom cryptography.hazmat.primitives.ciphers.modes import ECB\n\n\n# Insecure mode\nmode = ECB(iv)\n\n# Secure cipher and mode\ncipher = AES.new(key, blockalgo.MODE_CTR, iv)\n\n# Secure mode\nmode = CBC(iv)\n"
  },
  {
    "path": "examples/ciphers.py",
    "content": "from Crypto.Cipher import ARC2 as pycrypto_arc2\nfrom Crypto.Cipher import ARC4 as pycrypto_arc4\nfrom Crypto.Cipher import Blowfish as pycrypto_blowfish\nfrom Crypto.Cipher import DES as pycrypto_des\nfrom Crypto.Cipher import XOR as pycrypto_xor\nfrom Cryptodome.Cipher import ARC2 as pycryptodomex_arc2\nfrom Cryptodome.Cipher import ARC4 as pycryptodomex_arc4\nfrom Cryptodome.Cipher import Blowfish as pycryptodomex_blowfish\nfrom Cryptodome.Cipher import DES as pycryptodomex_des\nfrom Cryptodome.Cipher import XOR as pycryptodomex_xor\nfrom Crypto.Hash import SHA\nfrom Crypto import Random\nfrom Crypto.Util import Counter\nfrom cryptography.hazmat.primitives.ciphers import Cipher\nfrom cryptography.hazmat.primitives.ciphers import algorithms\nfrom cryptography.hazmat.primitives.ciphers import modes\nfrom cryptography.hazmat.backends import default_backend\nfrom struct import pack\n\nkey = b'Sixteen byte key'\niv = Random.new().read(pycrypto_arc2.block_size)\ncipher = pycrypto_arc2.new(key, pycrypto_arc2.MODE_CFB, iv)\nmsg = iv + cipher.encrypt(b'Attack at dawn')\ncipher = pycryptodomex_arc2.new(key, pycryptodomex_arc2.MODE_CFB, iv)\nmsg = iv + cipher.encrypt(b'Attack at dawn')\n\nkey = b'Very long and confidential key'\nnonce = Random.new().read(16)\ntempkey = SHA.new(key+nonce).digest()\ncipher = pycrypto_arc4.new(tempkey)\nmsg = nonce + cipher.encrypt(b'Open the pod bay doors, HAL')\ncipher = pycryptodomex_arc4.new(tempkey)\nmsg = nonce + cipher.encrypt(b'Open the pod bay doors, HAL')\n\niv = Random.new().read(bs)\nkey = b'An arbitrarily long key'\nplaintext = b'docendo discimus '\nplen = bs - divmod(len(plaintext),bs)[1]\npadding = [plen]*plen\npadding = pack('b'*plen, *padding)\nbs = pycrypto_blowfish.block_size\ncipher = pycrypto_blowfish.new(key, pycrypto_blowfish.MODE_CBC, iv)\nmsg = iv + cipher.encrypt(plaintext + padding)\nbs = pycryptodomex_blowfish.block_size\ncipher = pycryptodomex_blowfish.new(key, pycryptodomex_blowfish.MODE_CBC, iv)\nmsg = iv + cipher.encrypt(plaintext + padding)\n\nkey = b'-8B key-'\nplaintext = b'We are no longer the knights who say ni!'\nnonce = Random.new().read(pycrypto_des.block_size/2)\nctr = Counter.new(pycrypto_des.block_size*8/2, prefix=nonce)\ncipher = pycrypto_des.new(key, pycrypto_des.MODE_CTR, counter=ctr)\nmsg = nonce + cipher.encrypt(plaintext)\nnonce = Random.new().read(pycryptodomex_des.block_size/2)\nctr = Counter.new(pycryptodomex_des.block_size*8/2, prefix=nonce)\ncipher = pycryptodomex_des.new(key, pycryptodomex_des.MODE_CTR, counter=ctr)\nmsg = nonce + cipher.encrypt(plaintext)\n\nkey = b'Super secret key'\nplaintext = b'Encrypt me'\ncipher = pycrypto_xor.new(key)\nmsg = cipher.encrypt(plaintext)\ncipher = pycryptodomex_xor.new(key)\nmsg = cipher.encrypt(plaintext)\n\ncipher = Cipher(algorithms.ARC4(key), mode=None, backend=default_backend())\nencryptor = cipher.encryptor()\nct = encryptor.update(b\"a secret message\")\n\ncipher = Cipher(algorithms.Blowfish(key), mode=None, backend=default_backend())\nencryptor = cipher.encryptor()\nct = encryptor.update(b\"a secret message\")\n\ncipher = Cipher(algorithms.CAST5(key), mode=None, backend=default_backend())\nencryptor = cipher.encryptor()\nct = encryptor.update(b\"a secret message\")\n\ncipher = Cipher(algorithms.IDEA(key), mode=None, backend=default_backend())\nencryptor = cipher.encryptor()\nct = encryptor.update(b\"a secret message\")\n\ncipher = Cipher(algorithms.SEED(key), mode=None, backend=default_backend())\nencryptor = cipher.encryptor()\nct = encryptor.update(b\"a secret message\")\n\ncipher = Cipher(algorithms.TripleDES(key), mode=None, backend=default_backend())\nencryptor = cipher.encryptor()\nct = encryptor.update(b\"a secret message\")\n"
  },
  {
    "path": "examples/crypto-md5.py",
    "content": "from cryptography.hazmat.primitives import hashes\nfrom Crypto.Hash import MD2 as pycrypto_md2\nfrom Crypto.Hash import MD4 as pycrypto_md4\nfrom Crypto.Hash import MD5 as pycrypto_md5\nfrom Crypto.Hash import SHA as pycrypto_sha\nfrom Cryptodome.Hash import MD2 as pycryptodomex_md2\nfrom Cryptodome.Hash import MD4 as pycryptodomex_md4\nfrom Cryptodome.Hash import MD5 as pycryptodomex_md5\nfrom Cryptodome.Hash import SHA as pycryptodomex_sha\nimport hashlib\nimport crypt\n\nhashlib.md5(1)\nhashlib.md5(1).hexdigest()\n\nabc = str.replace(hashlib.md5(\"1\"), \"###\")\n\nprint(hashlib.md5(\"1\"))\n\nhashlib.sha1(1)\n\nhashlib.sha1(usedforsecurity=False)\n\npycrypto_md2.new()\npycrypto_md4.new()\npycrypto_md5.new()\npycrypto_sha.new()\n\npycryptodomex_md2.new()\npycryptodomex_md4.new()\npycryptodomex_md5.new()\npycryptodomex_sha.new()\n\nhashes.MD5()\nhashes.SHA1()\n\ncrypt.crypt(\"asdfasdfasdfasdf\", salt=crypt.METHOD_CRYPT)\ncrypt.crypt(\"asdfasdfasdfasdf\", salt=crypt.METHOD_MD5)\ncrypt.crypt(\"asdfasdfasdfasdf\", salt=crypt.METHOD_BLOWFISH)\ncrypt.crypt(\"asdfasdfasdfasdf\")\ncrypt.crypt(\"asdfasdfasdfasdf\", salt=crypt.METHOD_SHA256)\ncrypt.crypt(\"asdfasdfasdfasdf\", salt=crypt.METHOD_SHA512)\n\ncrypt.mksalt(crypt.METHOD_CRYPT)\ncrypt.mksalt(crypt.METHOD_MD5)\ncrypt.mksalt(crypt.METHOD_BLOWFISH)\ncrypt.mksalt()\ncrypt.mksalt(crypt.METHOD_SHA256)\ncrypt.mksalt(crypt.METHOD_SHA512)\n"
  },
  {
    "path": "examples/dill.py",
    "content": "import dill\nimport io\n\n# dill\npick = dill.dumps({'a': 'b', 'c': 'd'})\nprint(dill.loads(pick))\n\nfile_obj = io.BytesIO()\ndill.dump([1, 2, '3'], file_obj)\nfile_obj.seek(0)\nprint(dill.load(file_obj))\n\nfile_obj.seek(0)\nprint(dill.Unpickler(file_obj).load())\n"
  },
  {
    "path": "examples/django_sql_injection_extra.py",
    "content": "from django.contrib.auth.models import User\n\nUser.objects.filter(username='admin').extra(\n    select={'test': 'secure'},\n    where=['secure'],\n    tables=['secure']\n)\nUser.objects.filter(username='admin').extra({'test': 'secure'})\nUser.objects.filter(username='admin').extra(select={'test': 'secure'})\nUser.objects.filter(username='admin').extra(where=['secure'])\n\nUser.objects.filter(username='admin').extra(dict(could_be='insecure'))\nUser.objects.filter(username='admin').extra(select=dict(could_be='insecure'))\nquery = '\"username\") AS \"username\", * FROM \"auth_user\" WHERE 1=1 OR \"username\"=? --'\nUser.objects.filter(username='admin').extra(select={'test': query})\nUser.objects.filter(username='admin').extra(select={'test': '%secure' % 'nos'})\nUser.objects.filter(username='admin').extra(select={'test': '{}secure'.format('nos')})\n\nwhere_var = ['1=1) OR 1=1 AND (1=1']\nUser.objects.filter(username='admin').extra(where=where_var)\nwhere_str = '1=1) OR 1=1 AND (1=1'\nUser.objects.filter(username='admin').extra(where=[where_str])\nUser.objects.filter(username='admin').extra(where=['%secure' % 'nos'])\nUser.objects.filter(username='admin').extra(where=['{}secure'.format('no')])\n\ntables_var = ['django_content_type\" WHERE \"auth_user\".\"username\"=\"admin']\nUser.objects.all().extra(tables=tables_var).distinct()\ntables_str = 'django_content_type\" WHERE \"auth_user\".\"username\"=\"admin'\nUser.objects.all().extra(tables=[tables_str]).distinct()\n"
  },
  {
    "path": "examples/django_sql_injection_raw.py",
    "content": "from django.db.models.expressions import RawSQL\nfrom django.contrib.auth.models import User\n\nUser.objects.annotate(val=RawSQL('secure', []))\nUser.objects.annotate(val=RawSQL('%secure' % 'nos', []))\nUser.objects.annotate(val=RawSQL('{}secure'.format('no'), []))\nraw = '\"username\") AS \"val\" FROM \"auth_user\" WHERE \"username\"=\"admin\" --'\nUser.objects.annotate(val=RawSQL(raw, []))\nraw = '\"username\") AS \"val\" FROM \"auth_user\"' \\\n      ' WHERE \"username\"=\"admin\" OR 1=%s --'\nUser.objects.annotate(val=RawSQL(raw, [0]))\nUser.objects.annotate(val=RawSQL(sql='{}secure'.format('no'), params=[]))\nUser.objects.annotate(val=RawSQL(params=[], sql='{}secure'.format('no')))\n"
  },
  {
    "path": "examples/eval.py",
    "content": "import os\n\nprint(eval(\"1+1\"))\nprint(eval(\"os.getcwd()\"))\nprint(eval(\"os.chmod('%s', 0777)\" % 'test.txt'))\n\n\n# A user-defined method named \"eval\" should not get flagged.\nclass Test(object):\n    def eval(self):\n        print(\"hi\")\n    def foo(self):\n        self.eval()\n\nTest().eval()\n"
  },
  {
    "path": "examples/exec.py",
    "content": "exec(\"do evil\")\n"
  },
  {
    "path": "examples/flask_debug.py",
    "content": "from flask import Flask\n\napp = Flask(__name__)\n\n@app.route('/')\ndef main():\n    raise\n\n#bad\napp.run(debug=True)\n\n#okay\napp.run()\napp.run(debug=False)\n\n#unrelated\nrun()\nrun(debug=True)\nrun(debug)\n"
  },
  {
    "path": "examples/ftplib.py",
    "content": "from ftplib import FTP\nfrom ftplib import FTP_TLS\n\n\n# bad\nftp = FTP('ftp.debian.org')\nftp.login()\n\nftp.cwd('debian')\nftp.retrlines('LIST')\n\nftp.quit()\n\n# okay\nftp = ftplib.FTP_TLS(\n    \"ftp.us.debian.org\",\n    context=ssl.create_default_context(),\n)\nftp.login()\n\nftp.cwd(\"debian\")\nftp.retrlines(\"LIST\")\n\nftp.quit()\n"
  },
  {
    "path": "examples/hardcoded-passwords.py",
    "content": "# Possible hardcoded password: 'class_password'\n# Severity: Low   Confidence: Medium\nclass SomeClass:\n    password = \"class_password\"\n\n# Possible hardcoded password: 'Admin'\n# Severity: Low   Confidence: Medium\ndef someFunction(user, password=\"Admin\"):\n    print(\"Hi \" + user)\n\ndef someFunction2(password):\n    # Possible hardcoded password: 'root'\n    # Severity: Low   Confidence: Medium\n    if password == \"root\":\n        print(\"OK, logged in\")\n\ndef noMatch(password):\n    # Possible hardcoded password: ''\n    # Severity: Low   Confidence: Medium\n    if password == '':\n        print(\"No password!\")\n\ndef NoMatch2(password):\n    # Possible hardcoded password: 'ajklawejrkl42348swfgkg'\n    # Severity: Low   Confidence: Medium\n    if password == \"ajklawejrkl42348swfgkg\":\n        print(\"Nice password!\")\n\ndef noMatchObject():\n    obj = SomeClass()\n    # Possible hardcoded password: 'this cool password'\n    # Severity: Low   Confidence: Medium\n    if obj.password == \"this cool password\":\n        print(obj.password)\n\n# Possible hardcoded password: 'blerg'\n# Severity: Low   Confidence: Medium\ndef doLogin(password=\"blerg\"):\n    pass\n\ndef NoMatch3(a, b):\n    pass\n\n# Possible hardcoded password: 'blerg'\n# Severity: Low   Confidence: Medium\ndoLogin(password=\"blerg\")\n\n# Possible hardcoded password: 'blerg'\n# Severity: Low   Confidence: Medium\npassword = \"blerg\"\n\n# Possible hardcoded password: 'blerg'\n# Severity: Low   Confidence: Medium\npassword[\"password\"] = \"blerg\"\n\n# Possible hardcoded password: 'secret'\n# Severity: Low   Confidence: Medium\nEMAIL_PASSWORD = \"secret\"\n\n# Possible hardcoded password: 'emails_secret'\n# Severity: Low   Confidence: Medium\nemail_pwd = 'emails_secret'\n\n# Possible hardcoded password: 'd6s$f9g!j8mg7hw?n&2'\n# Severity: Low   Confidence: Medium\nmy_secret_password_for_email = 'd6s$f9g!j8mg7hw?n&2'\n\n# Possible hardcoded password: '1234'\n# Severity: Low   Confidence: Medium\npassphrase='1234'\n\n# Possible hardcoded password: None\n# Severity: High   Confidence: High\ndef __init__(self, auth_scheme, auth_token=None, auth_username=None, auth_password=None, auth_link=None, **kwargs):\n    self.auth_scheme = auth_scheme\n    self.auth_token = auth_token\n    self.auth_username = auth_username\n    self.auth_password = auth_password\n    self.auth_link = auth_link\n    self.kwargs = kwargs\n\n# Possible hardcoded password: None\n# Severity: High   Confidence: High\nfrom oslo_config import cfg\ncfg.StrOpt(\n    'metadata_proxy_shared_secret',\n    default='',\n    secret=True,\n)\n\n# Possible hardcoded password: 'pass'\n# Severity: Low   Confidence: Medium\n# https://github.com/PyCQA/bandit/issues/313\nlog({\"server\": server, \"password\": 'pass', \"user\": user})\n\n# ... but not:\nlog({\"server\": server, \"password\": password, \"user\": user})\n\n# Possible hardcoded password: '12345'\n# Severity: Low   Confidence: Medium\n# https://github.com/PyCQA/bandit/issues/1267\ninfo = {\"password\": \"12345\"}\n\n# ... but not:\ninfo = {\"password\": password}\n"
  },
  {
    "path": "examples/hardcoded-tmp.py",
    "content": "with open('/tmp/abc', 'w') as f:\n    f.write('def')\n\n# ok\nwith open('/abc/tmp', 'w') as f:\n    f.write('def')\n\nwith open('/var/tmp/123', 'w') as f:\n    f.write('def')\n\nwith open('/dev/shm/unit/test', 'w') as f:\n    f.write('def')\n\n# Negative test\nwith open('/foo/bar', 'w') as f:\n    f.write('def')\n"
  },
  {
    "path": "examples/hashlib_new_insecure_functions.py",
    "content": "import hashlib\n\nhashlib.new('md5')\n\nhashlib.new('md4', b'test')\n\nhashlib.new(name='md5', data=b'test')\n\nhashlib.new('MD4', data=b'test')\n\nhashlib.new('sha1')\n\nhashlib.new('sha1', data=b'test')\n\nhashlib.new('sha', data=b'test')\n\nhashlib.new(name='SHA', data=b'test')\n\nhashlib.new('sha1', usedforsecurity=True)\n\n# Test that plugin does not flag valid hash functions.\nhashlib.new('sha256')\n\nhashlib.new('SHA512')\n\nhashlib.new(name='sha1', usedforsecurity=False)\n"
  },
  {
    "path": "examples/httpoxy_cgihandler.py",
    "content": "import requests\nimport wsgiref.handlers\n\ndef application(environ, start_response):\n    r = requests.get('https://192.168.0.42/private/api/foobar', timeout=30)\n    start_response('200 OK', [('Content-Type', 'text/plain')])\n    return [r.content]\n\nif __name__ == '__main__':\n    wsgiref.handlers.CGIHandler().run(application)\n"
  },
  {
    "path": "examples/httpoxy_twisted_directory.py",
    "content": "from twisted.internet import reactor\nfrom twisted.web import static, server, twcgi\n\nroot = static.File(\"/root\")\nroot.putChild(\"cgi-bin\", twcgi.CGIDirectory(\"/var/www/cgi-bin\"))\nreactor.listenTCP(80, server.Site(root))\nreactor.run()\n"
  },
  {
    "path": "examples/httpoxy_twisted_script.py",
    "content": "from twisted.internet import reactor\nfrom twisted.web import static, server, twcgi\n\nroot = static.File(\"/root\")\nroot.putChild(\"login.cgi\", twcgi.CGIScript(\"/var/www/cgi-bin/login.py\"))\nreactor.listenTCP(80, server.Site(root))\nreactor.run()\n"
  },
  {
    "path": "examples/huggingface_unsafe_download.py",
    "content": "from datasets import load_dataset\nfrom huggingface_hub import hf_hub_download, snapshot_download\nfrom transformers import AutoModel, AutoTokenizer\n\n# UNSAFE USAGE\n\n# AutoModel (Model Loading)\n\n# Example #1: No revision (defaults to floating 'main')\nunsafe_model_no_revision = AutoModel.from_pretrained(\"org/model_name\")\n\n# Example #2: Floating revision: 'main'\nunsafe_model_main = AutoModel.from_pretrained(\n    \"org/model_name\",\n    revision=\"main\"\n)\n\n# Example #3: Floating tag revision: 'v1.0.0'\nunsafe_model_tag = AutoModel.from_pretrained(\n    \"org/model_name\",\n    revision=\"v1.0.0\"\n)\n\n\n# AutoTokenizer (Tokenizer Loading)\n\n# Example #4: No revision\nunsafe_tokenizer_no_revision = AutoTokenizer.from_pretrained(\"org/model_name\")\n\n# Example #5: Floating revision: 'main'\nunsafe_tokenizer_main = AutoTokenizer.from_pretrained(\n    \"org/model_name\",\n    revision=\"main\"\n)\n\n# Example #6: Floating tag revision: 'v1.0.0'\nunsafe_tokenizer_tag = AutoTokenizer.from_pretrained(\n    \"org/model_name\",\n    revision=\"v1.0.0\"\n)\n\n\n# Example #7: load_dataset (Dataset Loading)\n\n# Example #8: No revision\nunsafe_dataset_no_revision = load_dataset(\"org_dataset\")\n\n# Example #9: Floating revision: 'main'\nunsafe_dataset_main = load_dataset(\"org_dataset\", revision=\"main\")\n\n# Example #10: Floating tag revision: 'v1.0.0'\nunsafe_dataset_tag = load_dataset(\"org_dataset\", revision=\"v1.0.0\")\n\n\n# f_hub_download (File Download)\n\n# Example #11: No revision\nunsafe_file_no_revision = hf_hub_download(\n    repo_id=\"org/model_name\",\n    filename=\"config.json\"\n)\n\n# Example #12: Floating revision: 'main'\nunsafe_file_main = hf_hub_download(\n    repo_id=\"org/model_name\",\n    filename=\"config.json\",\n    revision=\"main\"\n)\n\n# Example #13: Floating tag revision: 'v1.0.0'\nunsafe_file_tag = hf_hub_download(\n    repo_id=\"org/model_name\",\n    filename=\"config.json\",\n    revision=\"v1.0.0\"\n)\n\n\n# snapshot_download (Repo Snapshot)\n\n# Example #14: No revision\nunsafe_snapshot_no_revision = snapshot_download(repo_id=\"org/model_name\")\n\n# Example #15: Floating revision: 'main'\nunsafe_snapshot_main = snapshot_download(\n    repo_id=\"org/model_name\",\n    revision=\"main\"\n)\n\n# Example #16: Floating tag revision: 'v1.0.0'\nunsafe_snapshot_tag = snapshot_download(\n    repo_id=\"org/model_name\",\n    revision=\"v1.0.0\"\n)\n\n\n# -------------------------------\n# SAFE USAGE\n# -------------------------------\n\n# AutoModel\n\n# Example #17: Pinned commit hash\nsafe_model_commit = AutoModel.from_pretrained(\n    \"org/model_name\",\n    revision=\"5d0f2e8a7f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d\"\n)\n\n# Example #18: Local path\nsafe_model_local = AutoModel.from_pretrained(\"./local_model\")\nsafe_model_local_abs = AutoModel.from_pretrained(\"/path/to/model\")\n\n# AutoTokenizer\n\n# Example #19: Pinned commit hash\nsafe_tokenizer_commit = AutoTokenizer.from_pretrained(\n    \"org/model_name\",\n    revision=\"5d0f2e8a7f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d\"\n)\n\n# Example #20: Local path\nsafe_tokenizer_local = AutoTokenizer.from_pretrained(\"./local_tokenizer\")\n\n\n# load_dataset\n\n# Example #21: Pinned commit hash\nsafe_dataset_commit = load_dataset(\n    \"org_dataset\",\n    revision=\"5d0f2e8a7f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d\"\n)\n\n\n# hf_hub_download\n\n# Example #22: Pinned commit hash\nsafe_file_commit = hf_hub_download(\n    repo_id=\"org/model_name\",\n    filename=\"config.json\",\n    revision=\"5d0f2e8a7f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d\"\n)\n\n\n# snapshot_download\n\n# Example #23: Pinned commit hash\nsafe_snapshot_commit = snapshot_download(\n    repo_id=\"org/model_name\",\n    revision=\"5d0f2e8a7f1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d\"\n)\n\n\n# Example #24: Revision passed as a variable (can't be statically checked)\nMODEL_REVISION = \"548fc3543a\"\nsafe_model_variable = AutoModel.from_pretrained(\n    \"org/model_name\",\n    revision=MODEL_REVISION\n)\n\n# Example #25: Revision from a dict/subscript access\nconfig = {\"revision\": \"abc1234567\"}\nsafe_model_subscript = AutoModel.from_pretrained(\n    \"org/model_name\",\n    revision=config[\"revision\"]\n)\n"
  },
  {
    "path": "examples/imports-aliases.py",
    "content": "from subprocess import Popen as pop\nimport hashlib as h\nimport hashlib as hh\nimport hashlib as hhh\nimport hashlib as hhhh\nfrom pickle import loads as lp\nimport pickle as p\n\npop('/bin/gcc --version', shell=True)\n\nh.md5('1')\nhh.md5('2')\nhhh.md5('3').hexdigest()\nhhhh.md5('4')\nlp({'key': 'value'})\n"
  },
  {
    "path": "examples/imports-from.py",
    "content": "from subprocess import Popen\n\nfrom ..foo import sys\nfrom . import sys\nfrom .. import sys\nfrom .. import subprocess\nfrom ..subprocess import Popen\n"
  },
  {
    "path": "examples/imports-function.py",
    "content": "os = __import__(\"os\")\npickle = __import__(\"pickle\")\nsys = __import__(\"sys\")\nsubprocess = __import__(\"subprocess\")\n\n# this has been reported in the wild, though it's invalid python\n# see bug https://bugs.launchpad.net/bandit/+bug/1396333\n__import__()\n\n# TODO(??): bandit can not find this one unfortunately (no symbol tab)\na = 'subprocess'\n__import__(a)\n"
  },
  {
    "path": "examples/imports-with-importlib.py",
    "content": "import importlib\na = importlib.import_module('os')\nb = importlib.import_module('pickle')\nc = importlib.__import__('sys')\nd = importlib.__import__('subprocess')\n\n# Do not crash when target is an expression\ne = importlib.import_module(MODULE_MAP[key])\nf = importlib.__import__(MODULE_MAP[key])\n\n# Do not crash when target is a named argument\ng = importlib.import_module(name='sys')\nh = importlib.__import__(name='subprocess')\ni = importlib.import_module(name='subprocess', package='bar.baz')\nj = importlib.__import__(name='sys', package='bar.baz')\n"
  },
  {
    "path": "examples/imports.py",
    "content": "import os\nimport pickle\nimport sys\nimport subprocess\n"
  },
  {
    "path": "examples/init-py-test/__init__.py",
    "content": ""
  },
  {
    "path": "examples/init-py-test/subdirectory-okay.py",
    "content": "# A sample test file in a subdirectory and its parents both containing\n# an __init__.py file outlined in bug/1743042.\nprint('hopefully no vulnerabilities here')\n"
  },
  {
    "path": "examples/jinja2_templating.py",
    "content": "import jinja2\nfrom jinja2 import Environment, select_autoescape\ntemplateLoader = jinja2.FileSystemLoader( searchpath=\"/\" )\nsomething = ''\n\nEnvironment(loader=templateLoader, load=templateLoader, autoescape=True)\ntemplateEnv = jinja2.Environment(autoescape=True,\n        loader=templateLoader )\nEnvironment(loader=templateLoader, load=templateLoader, autoescape=something)\ntemplateEnv = jinja2.Environment(autoescape=False, loader=templateLoader )\nEnvironment(loader=templateLoader,\n            load=templateLoader,\n            autoescape=False)\n\nEnvironment(loader=templateLoader,\n            load=templateLoader)\n\nEnvironment(loader=templateLoader, autoescape=select_autoescape())\n\nEnvironment(loader=templateLoader,\n            autoescape=select_autoescape(['html', 'htm', 'xml']))\n\nEnvironment(loader=templateLoader,\n            autoescape=jinja2.select_autoescape(['html', 'htm', 'xml']))\n\n\ndef fake_func():\n    return 'foobar'\nEnvironment(loader=templateLoader, autoescape=fake_func())\n"
  },
  {
    "path": "examples/jsonpickle.py",
    "content": "import jsonpickle\n\n\npick = jsonpickle.encode({'a': 'b', 'c': 'd'})\n\nprint(jsonpickle.decode(pick))\n\nprint(jsonpickle.unpickler.decode(pick))\n\nprint(jsonpickle.unpickler.Unpickler().restore(pick))\n"
  },
  {
    "path": "examples/logging_config_insecure_listen.py",
    "content": "import logging.config\n\nt = logging.config.listen(9999)\n"
  },
  {
    "path": "examples/long_set.py",
    "content": "# This file contains a single long_set with 7276 'a' elements\nlong_set = {\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a',\n    'a'\n}\n"
  },
  {
    "path": "examples/mako_templating.py",
    "content": "from mako.template import Template\nimport mako\n\nfrom mako import template\n\nTemplate(\"hello\")\n\n# XXX(fletcher): for some reason, bandit is missing the one below. keeping it\n# in for now so that if it gets fixed inadvertitently we know.\nmako.template.Template(\"hern\")\ntemplate.Template(\"hern\")\n"
  },
  {
    "path": "examples/mark_safe.py",
    "content": "from django.utils import safestring\n\nmystr = '<b>Hello World</b>'\nmystr = safestring.mark_safe(mystr)\n"
  },
  {
    "path": "examples/mark_safe_insecure.py",
    "content": "import os\nfrom django.utils import safestring\n\n\ndef insecure_function(text, cls=''):\n    return '<h1 class=\"{cls}\">{text}</h1>'.format(text=text, cls=cls)\n\n\nmy_insecure_str = insecure_function('insecure', cls='\" onload=\"alert(\\'xss\\')')\nsafestring.mark_safe(my_insecure_str)\nsafestring.SafeText(my_insecure_str)\nsafestring.SafeUnicode(my_insecure_str)\nsafestring.SafeString(my_insecure_str)\nsafestring.SafeBytes(my_insecure_str)\n\n\ndef try_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    try:\n        my_insecure_str = insecure_function('insecure', cls=cls)\n    except Exception:\n        my_insecure_str = 'Secure'\n    safestring.mark_safe(my_insecure_str)\n\n\ndef except_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    try:\n        my_insecure_str = 'Secure'\n    except Exception:\n        my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe(my_insecure_str)\n\n\ndef try_else_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    try:\n        if 1 == random.randint(0, 1):  # nosec\n            raise Exception\n    except Exception:\n        my_insecure_str = 'Secure'\n    else:\n        my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe(my_insecure_str)\n\n\ndef finally_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    try:\n        if 1 == random.randint(0, 1):  # nosec\n            raise Exception\n    except Exception:\n        print(\"Exception\")\n    else:\n        print(\"No Exception\")\n    finally:\n        my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe(my_insecure_str)\n\n\ndef format_arg_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>{} {}</b>'.format(my_insecure_str, 'STR'))\n\n\ndef format_startarg_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>{}</b>'.format(*[my_insecure_str]))\n\n\ndef format_keywords_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>{b}</b>'.format(b=my_insecure_str))\n\n\ndef format_kwargs_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>{b}</b>'.format(**{'b': my_insecure_str}))\n\n\ndef percent_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>%s</b>' % my_insecure_str)\n\n\ndef percent_list_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>%s %s</b>' % (my_insecure_str, 'b'))\n\n\ndef percent_dict_insecure(cls='\" onload=\"alert(\\'xss\\')'):\n    my_insecure_str = insecure_function('insecure', cls=cls)\n    safestring.mark_safe('<b>%(b)s</b>' % {'b': my_insecure_str})\n\n\ndef import_insecure():\n    import sre_constants\n    safestring.mark_safe(sre_constants.ANY)\n\n\ndef import_as_insecure():\n    import sre_constants.ANY as any_str\n    safestring.mark_safe(any_str)\n\n\ndef from_import_insecure():\n    from sre_constants import ANY\n    safestring.mark_safe(ANY)\n\n\ndef from_import_as_insecure():\n    from sre_constants import ANY as any_str\n    safestring.mark_safe(any_str)\n\n\ndef with_insecure(path):\n    with open(path) as f:\n        safestring.mark_safe(f.read())\n\n\ndef also_with_insecure(path):\n    with open(path) as f:\n        safestring.mark_safe(f)\n\n\ndef for_insecure():\n    my_secure_str = ''\n    for i in range(random.randint(0, 1)):  # nosec\n        my_secure_str += insecure_function('insecure', cls='\" onload=\"alert(\\'xss\\')')\n    safestring.mark_safe(my_secure_str)\n\n\ndef while_insecure():\n    my_secure_str = ''\n    while ord(os.urandom(1)) % 2 == 0:\n        my_secure_str += insecure_function('insecure', cls='\" onload=\"alert(\\'xss\\')')\n    safestring.mark_safe(my_secure_str)\n\n\ndef some_insecure_case():\n    if ord(os.urandom(1)) % 2 == 0:\n        my_secure_str = insecure_function('insecure', cls='\" onload=\"alert(\\'xss\\')')\n    elif ord(os.urandom(1)) % 2 == 0:\n        my_secure_str = 'Secure'\n    else:\n        my_secure_str = 'Secure'\n    safestring.mark_safe(my_secure_str)\n\nmystr = 'insecure'\n\n\ndef test_insecure_shadow():  # var assigned out of scope\n    safestring.mark_safe(mystr)\n\n\ndef test_insecure(str_arg):\n    safestring.mark_safe(str_arg)\n\n\ndef test_insecure_with_assign(str_arg=None):\n    if not str_arg:\n        str_arg = 'could be insecure'\n    safestring.mark_safe(str_arg)\n\ndef test_insecure_tuple_assign():\n    HTML_CHOICES = (\n        (_('Donate'), 'https://example.org/donate/'),\n        (_('More info'), 'https://example.org/'),\n    )\n    text, url = choice(HTML_CHOICES)\n    safestring.mark_safe('<a href=\"{0}\">{1}</a>'.format(url, text))\n"
  },
  {
    "path": "examples/mark_safe_secure.py",
    "content": "import os\nfrom django.utils import safestring\n\nsafestring.mark_safe('<b>secure</b>')\nsafestring.SafeText('<b>secure</b>')\nsafestring.SafeUnicode('<b>secure</b>')\nsafestring.SafeString('<b>secure</b>')\nsafestring.SafeBytes('<b>secure</b>')\n\nmy_secure_str = '<b>Hello World</b>'\nsafestring.mark_safe(my_secure_str)\n\nmy_secure_str, _ = ('<b>Hello World</b>', '')\nsafestring.mark_safe(my_secure_str)\n\nalso_secure_str = my_secure_str\nsafestring.mark_safe(also_secure_str)\n\n\ndef try_secure():\n    try:\n        my_secure_str = 'Secure'\n    except Exception:\n        my_secure_str = 'Secure'\n    else:\n        my_secure_str = 'Secure'\n    finally:\n        my_secure_str = 'Secure'\n    safestring.mark_safe(my_secure_str)\n\n\ndef format_secure():\n    safestring.mark_safe('<b>{}</b>'.format('secure'))\n    my_secure_str = 'secure'\n    safestring.mark_safe('<b>{}</b>'.format(my_secure_str))\n    safestring.mark_safe('<b>{} {}</b>'.format(my_secure_str, 'a'))\n    safestring.mark_safe('<b>{} {}</b>'.format(*[my_secure_str, 'a']))\n    safestring.mark_safe('<b>{b}</b>'.format(b=my_secure_str))  # nosec TODO\n    safestring.mark_safe('<b>{b}</b>'.format(**{'b': my_secure_str}))  # nosec TODO\n    my_secure_str = '<b>{}</b>'.format(my_secure_str)\n    safestring.mark_safe(my_secure_str)\n\n\ndef percent_secure():\n    safestring.mark_safe('<b>%s</b>' % 'secure')\n    my_secure_str = 'secure'\n    safestring.mark_safe('<b>%s</b>' % my_secure_str)\n    safestring.mark_safe('<b>%s %s</b>' % (my_secure_str, 'a'))\n    safestring.mark_safe('<b>%(b)s</b>' % {'b': my_secure_str})  # nosec TODO\n\n\ndef with_secure(path):\n    with open(path) as f:\n        safestring.mark_safe('Secure')\n\n\ndef loop_secure():\n    my_secure_str = ''\n\n    for i in range(ord(os.urandom(1))):\n        my_secure_str += ' Secure'\n    safestring.mark_safe(my_secure_str)\n    while ord(os.urandom(1)) % 2 == 0:\n        my_secure_str += ' Secure'\n    safestring.mark_safe(my_secure_str)\n\n\ndef all_secure_case():\n    if ord(os.urandom(1)) % 2 == 0:\n        my_secure_str = 'Secure'\n    elif ord(os.urandom(1)) % 2 == 0:\n        my_secure_str = 'Secure'\n    else:\n        my_secure_str = 'Secure'\n    safestring.mark_safe(my_secure_str)\n"
  },
  {
    "path": "examples/markupsafe_markup_xss.py",
    "content": "import flask\nfrom markupsafe import Markup, escape\n\ncontent = \"<script>alert('Hello, world!')</script>\"\nMarkup(f\"unsafe {content}\")  # B704\nflask.Markup(\"unsafe {}\".format(content))  # B704\nMarkup(\"safe {}\").format(content)\nflask.Markup(b\"safe {}\", encoding='utf-8').format(content)\nescape(content)\nMarkup(content)  # B704\nflask.Markup(\"unsafe %s\" % content)  # B704\nMarkup(object=\"safe\")\nMarkup(object=\"unsafe {}\".format(content))  # Not currently detected\n"
  },
  {
    "path": "examples/markupsafe_markup_xss_allowed_calls.py",
    "content": "from bleach import clean\nfrom markupsafe import Markup\n\ncontent = \"<script>alert('Hello, world!')</script>\"\nMarkup(clean(content))\n\n# indirect assignments are currently not supported\ncleaned = clean(content)\nMarkup(cleaned)\n"
  },
  {
    "path": "examples/markupsafe_markup_xss_extend_markup_names.py",
    "content": "from markupsafe import Markup\nfrom webhelpers.html import literal\n\ncontent = \"<script>alert('Hello, world!')</script>\"\nMarkup(f\"unsafe {content}\")\nliteral(f\"unsafe {content}\")\n"
  },
  {
    "path": "examples/marshal_deserialize.py",
    "content": "import marshal\nimport tempfile\n\n\nserialized = marshal.dumps({'a': 1})\nprint(marshal.loads(serialized))\n\nfile_obj = tempfile.TemporaryFile()\nmarshal.dump(range(5), file_obj)\nfile_obj.seek(0)\nprint(marshal.load(file_obj))\nfile_obj.close()\n"
  },
  {
    "path": "examples/mktemp.py",
    "content": "from tempfile import mktemp\nimport tempfile.mktemp as mt\nimport tempfile as tmp\n\nfoo = 'hi'\n\nmktemp(foo)\ntempfile.mktemp('foo')\nmt(foo)\ntmp.mktemp(foo)\n"
  },
  {
    "path": "examples/multiline_statement.py",
    "content": "import subprocess\n\nsubprocess.check_output(\"/some_command\",\n                        \"args\",\n                        shell=True,\n                        universal_newlines=True)\n\nsubprocess.check_output(\n    \"/some_command\",\n    \"args\",\n    shell=True,\n    universal_newlines=True\n)\n"
  },
  {
    "path": "examples/new_candidates-all.py",
    "content": "import xml\nimport yaml\n\ndef subprocess_shell_cmd():\n    # sample function with known subprocess shell cmd candidates\n    # candidate #1\n    subprocess.Popen('/bin/ls *', shell=True)\n    # candidate #2\n    subprocess.Popen('/bin/ls *', shell=True) # nosec\n\ndef yaml_load():\n    # sample function with known yaml.load candidates\n    temp_str = yaml.dump({'a': '1', 'b': '2'})\n    # candidate #3\n    y = yaml.load(temp_str)\n    # candidate #4\n    y = yaml.load(temp_str) # nosec\n\ndef xml_sax_make_parser():\n    # sample function with known xml.sax.make_parser candidates\n    # candidate #5\n    xml.sax.make_parser()\n    # candidate #6\n    xml.sax.make_parser() # nosec\n"
  },
  {
    "path": "examples/new_candidates-none.py",
    "content": "def subprocess_shell_cmd():\n    # sample function with known subprocess shell cmd candidates\n\ndef yaml_load():\n    # sample function with known yaml.load candidates\n\ndef xml_sax_make_parser():\n    # sample function with known xml.sax.make_parser candidates\n"
  },
  {
    "path": "examples/new_candidates-nosec.py",
    "content": "import xml\nimport yaml\n\ndef subprocess_shell_cmd():\n    # sample function with known subprocess shell cmd candidates\n    # candidate #2\n    subprocess.Popen('/bin/ls *', shell=True) # nosec\n\ndef yaml_load():\n    # sample function with known yaml.load candidates\n    temp_str = yaml.dump({'a': '1', 'b': '2'})\n    # candidate #4\n    y = yaml.load(temp_str) # nosec\n\ndef xml_sax_make_parser():\n    # sample function with known xml.sax.make_parser candidates\n    # candidate #6\n    xml.sax.make_parser() # nosec\n"
  },
  {
    "path": "examples/new_candidates-some.py",
    "content": "import xml\nimport yaml\n\ndef subprocess_shell_cmd():\n    # sample function with known subprocess shell cmd candidates\n    # candidate #1\n    subprocess.Popen('/bin/ls *', shell=True)\n    # candidate #2\n    subprocess.Popen('/bin/ls *', shell=True) # nosec\n\ndef yaml_load():\n    # sample function with known yaml.load candidates\n    temp_str = yaml.dump({'a': '1', 'b': '2'})\n    # candidate #4\n    y = yaml.load(temp_str) # nosec\n\ndef xml_sax_make_parser():\n    # sample function with known xml.sax.make_parser candidates\n    # candidate #6\n    xml.sax.make_parser() # nosec\n"
  },
  {
    "path": "examples/no_host_key_verification.py",
    "content": "from paramiko import client\nfrom paramiko import AutoAddPolicy\nfrom paramiko import WarningPolicy\n\nssh_client = client.SSHClient()\nssh_client.set_missing_host_key_policy(client.AutoAddPolicy)\nssh_client.set_missing_host_key_policy(client.WarningPolicy)\nssh_client.set_missing_host_key_policy(client.AutoAddPolicy())\nssh_client.set_missing_host_key_policy(client.WarningPolicy())\n\nssh_client.set_missing_host_key_policy(AutoAddPolicy)\nssh_client.set_missing_host_key_policy(WarningPolicy)\nssh_client.set_missing_host_key_policy(AutoAddPolicy())\nssh_client.set_missing_host_key_policy(WarningPolicy())\n"
  },
  {
    "path": "examples/nonsense.py",
    "content": "test(hi\n"
  },
  {
    "path": "examples/nosec.py",
    "content": "import subprocess  # nosec: import_subprocess\nfrom cryptography.hazmat.primitives import hashes\nhashes.SHA1()  # nosec: md5\nsubprocess.Popen('/bin/ls *', shell=True) #nosec (on the line)\nsubprocess.Popen('/bin/ls *', #nosec (at the start of function call)\n                 shell=True)\nsubprocess.Popen('/bin/ls *',\n                 shell=True)  #nosec (on the specific kwarg line)\nsubprocess.Popen('#nosec', shell=True)\nsubprocess.Popen('/bin/ls *', shell=True) # type: ... # nosec # noqa: E501 ; pylint: disable=line-too-long\nsubprocess.Popen('/bin/ls *', shell=True) # type: ... # nosec B607 # noqa: E501 ; pylint: disable=line-too-long\nsubprocess.Popen('/bin/ls *', shell=True)  #nosec subprocess_popen_with_shell_equals_true (on the line)\nsubprocess.Popen('#nosec', shell=True) # nosec B607, B602\nsubprocess.Popen('#nosec', shell=True) # nosec B607 B602\nsubprocess.Popen('/bin/ls *', shell=True)  # nosec subprocess_popen_with_shell_equals_true start_process_with_partial_path\nsubprocess.Popen('/bin/ls *', shell=True) # type: ... # noqa: E501 ; pylint: disable=line-too-long # nosec\nsubprocess.Popen('#nosec', shell=True) # nosec B607, B101\nsubprocess.Popen('#nosec', shell=True) # nosec B602, subprocess_popen_with_shell_equals_true\n"
  },
  {
    "path": "examples/okay.py",
    "content": "print('hopefully no vulnerabilities here')\n"
  },
  {
    "path": "examples/os-chmod.py",
    "content": "import os\nimport stat\n\nkeyfile = 'foo'\n\nos.chmod('/etc/passwd', 0o227)\nos.chmod('/etc/passwd', 0o7)\nos.chmod('/etc/passwd', 0o664)\nos.chmod('/etc/passwd', 0o777)\nos.chmod('/etc/passwd', 0o770)\nos.chmod('/etc/passwd', 0o776)\nos.chmod('/etc/passwd', 0o760)\nos.chmod('~/.bashrc', 511)\nos.chmod('/etc/hosts', 0o777)\nos.chmod('/tmp/oh_hai', 0x1ff)\nos.chmod('/etc/passwd', stat.S_IRWXU)\nos.chmod(keyfile, 0o777)\nos.chmod('~/hidden_exec', stat.S_IXGRP)\nos.chmod('~/hidden_exec', stat.S_IXOTH)\n"
  },
  {
    "path": "examples/os-exec.py",
    "content": "import os\n\nos.execl(path, arg0, arg1)\nos.execle(path, arg0, arg1, env)\nos.execlp(file, arg0, arg1)\nos.execlpe(file, arg0, arg1, env)\nos.execv(path, args)\nos.execve(path, args, env)\nos.execvp(file, args)\nos.execvpe(file, args, env)\n\n"
  },
  {
    "path": "examples/os-popen.py",
    "content": "import os\nfrom os import popen\nimport os as o\nfrom os import popen as pos\n\nos.popen('/bin/uname -av')\npopen('/bin/uname -av')\no.popen('/bin/uname -av')\npos('/bin/uname -av')\nos.popen2('/bin/uname -av')\nos.popen3('/bin/uname -av')\nos.popen4('/bin/uname -av')\n\nos.popen4('/bin/uname -av; rm -rf /')\nos.popen4(some_var)\n"
  },
  {
    "path": "examples/os-spawn.py",
    "content": "import os\n\nos.spawnl(mode, path)\nos.spawnle(mode, path, env)\nos.spawnlp(mode, file)\nos.spawnlpe(mode, file, env)\nos.spawnv(mode, path, args)\nos.spawnve(mode, path, args, env)\nos.spawnvp(mode, file, args)\nos.spawnvpe(mode, file, args, env)\n"
  },
  {
    "path": "examples/os-startfile.py",
    "content": "import os\n\nos.startfile('/bin/foo.docx')\nos.startfile('/bin/bad.exe')\nos.startfile('/bin/text.txt')\n"
  },
  {
    "path": "examples/os_system.py",
    "content": "import os\n\nos.system('/bin/echo hi')\n"
  },
  {
    "path": "examples/pandas_read_pickle.py",
    "content": "import pickle\nimport pandas as pd\n\n\ndf = pd.DataFrame(\n    {\n        \"col_A\": [1, 2]\n    }\n)\npick = pickle.dumps(df)\n\nprint(pd.read_pickle(pick))\n"
  },
  {
    "path": "examples/paramiko_injection.py",
    "content": "import paramiko\n\n\nclient = paramiko.client.SSHClient()\n\n# this is not safe\nclient.exec_command('something; really; unsafe')\n\n# this is safe\nclient.connect('somehost')\n"
  },
  {
    "path": "examples/partial_path_process.py",
    "content": "from subprocess import Popen as pop\n\npop('gcc --version', shell=False)\npop('/bin/gcc --version', shell=False)\npop(var, shell=False)\n\npop(['ls', '-l'], shell=False)\npop(['/bin/ls', '-l'], shell=False)\n\npop('../ls -l', shell=False)\n\npop('c:\\\\hello\\\\something', shell=False)\npop('c:/hello/something_else', shell=False)\n"
  },
  {
    "path": "examples/pickle_deserialize.py",
    "content": "import io\nimport pickle\n\n\n# pickle\npick = pickle.dumps({'a': 'b', 'c': 'd'})\nprint(pickle.loads(pick))\n\nfile_obj = io.BytesIO()\npickle.dump([1, 2, '3'], file_obj)\nfile_obj.seek(0)\nprint(pickle.load(file_obj))\n\nfile_obj.seek(0)\nprint(pickle.Unpickler(file_obj).load())\n"
  },
  {
    "path": "examples/popen_wrappers.py",
    "content": "import commands\nimport popen2\n\n\nprint(commands.getstatusoutput('/bin/echo / | xargs ls'))\nprint(commands.getoutput('/bin/echo / | xargs ls'))\n\n# This one is safe.\nprint(commands.getstatus('/bin/echo / | xargs ls'))\n\nprint(popen2.popen2('/bin/echo / | xargs ls')[0].read())\nprint(popen2.popen3('/bin/echo / | xargs ls')[0].read())\nprint(popen2.popen4('/bin/echo / | xargs ls')[0].read())\nprint(popen2.Popen3('/bin/echo / | xargs ls').fromchild.read())\nprint(popen2.Popen4('/bin/echo / | xargs ls').fromchild.read())\n"
  },
  {
    "path": "examples/pycrypto.py",
    "content": "from Crypto.Cipher import AES\nfrom Crypto import Random\n\nfrom . import CryptoMaterialsCacheEntry\n\n\ndef test_pycrypto():\n    key = b'Sixteen byte key'\n    iv = Random.new().read(AES.block_size)\n    cipher = pycrypto_arc2.new(key, AES.MODE_CFB, iv)\n    factory = CryptoMaterialsCacheEntry()\n"
  },
  {
    "path": "examples/pycryptodome.py",
    "content": "from Cryptodome.Cipher import AES\nfrom Cryptodome import Random\n\nfrom . import CryptoMaterialsCacheEntry\n\n\ndef test_pycrypto():\n    key = b'Sixteen byte key'\n    iv = Random.new().read(AES.block_size)\n    cipher = pycrypto_arc2.new(key, AES.MODE_CFB, iv)\n    factory = CryptoMaterialsCacheEntry()\n"
  },
  {
    "path": "examples/pyghmi.py",
    "content": "from pyghmi.ipmi import command\n\ncmd = command.Command(bmc=\"bmc\",\n                      userid=\"userid\",\n                      password=\"ZjE4ZjI0NTE4YmI2NGJjZDliOGY3ZmJiY2UyN2IzODQK\")\n"
  },
  {
    "path": "examples/pytorch_load.py",
    "content": "import torch\nimport torchvision.models as models\n\n# Example of saving a model\nmodel = models.resnet18(pretrained=True)\ntorch.save(model.state_dict(), 'model_weights.pth')\n\n# Example of loading the model weights in an insecure way (should trigger B614)\nloaded_model = models.resnet18()\nloaded_model.load_state_dict(torch.load('model_weights.pth'))\n\n# Example of loading with weights_only=True (should NOT trigger B614)\nsafe_model = models.resnet18()\nsafe_model.load_state_dict(torch.load('model_weights.pth', weights_only=True))\n\n# Example of loading with weights_only=False (should trigger B614)\nunsafe_model = models.resnet18()\nunsafe_model.load_state_dict(torch.load('model_weights.pth', weights_only=False))\n\n# Example of loading with map_location but no weights_only (should trigger B614)\ncpu_model = models.resnet18()\ncpu_model.load_state_dict(torch.load('model_weights.pth', map_location='cpu'))\n\n# Example of loading with both map_location and weights_only=True (should NOT trigger B614)\nsafe_cpu_model = models.resnet18()\nsafe_cpu_model.load_state_dict(torch.load('model_weights.pth', map_location='cpu', weights_only=True))\n\n# Example of a torch.*.load call that should NOT trigger B614\n# Only pickle deserializers should trigger B614\ntorch.utils.cpp_extension.load(name=\"example_ext\", sources=[])\n"
  },
  {
    "path": "examples/random_module.py",
    "content": "import random\nimport os\nimport somelib\n\nbad = random.Random()\nbad = random.random()\nbad = random.randrange()\nbad = random.randint()\nbad = random.choice()\nbad = random.choices()\nbad = random.uniform()\nbad = random.triangular()\nbad = random.randbytes()\nbad = random.sample()\nbad = random.randrange()\nbad = random.getrandbits()\n\ngood = os.urandom()\ngood = random.SystemRandom()\n\nunknown = random()\nunknown = somelib.a.random()\n"
  },
  {
    "path": "examples/requests-missing-timeout.py",
    "content": "import httpx\nimport requests\nimport not_requests\n\n# Errors\nrequests.get('https://gmail.com')\nrequests.get('https://gmail.com', timeout=None)\nrequests.post('https://gmail.com')\nrequests.post('https://gmail.com', timeout=None)\nrequests.put('https://gmail.com')\nrequests.put('https://gmail.com', timeout=None)\nrequests.delete('https://gmail.com')\nrequests.delete('https://gmail.com', timeout=None)\nrequests.patch('https://gmail.com')\nrequests.patch('https://gmail.com', timeout=None)\nrequests.options('https://gmail.com')\nrequests.options('https://gmail.com', timeout=None)\nrequests.head('https://gmail.com')\nrequests.head('https://gmail.com', timeout=None)\nhttpx.get('https://gmail.com')\nhttpx.get('https://gmail.com', timeout=None)\nhttpx.post('https://gmail.com')\nhttpx.post('https://gmail.com', timeout=None)\nhttpx.put('https://gmail.com')\nhttpx.put('https://gmail.com', timeout=None)\nhttpx.delete('https://gmail.com')\nhttpx.delete('https://gmail.com', timeout=None)\nhttpx.patch('https://gmail.com')\nhttpx.patch('https://gmail.com', timeout=None)\nhttpx.options('https://gmail.com')\nhttpx.options('https://gmail.com', timeout=None)\nhttpx.head('https://gmail.com')\nhttpx.head('https://gmail.com', timeout=None)\nhttpx.Client()\nhttpx.Client(timeout=None)\nhttpx.AsyncClient()\nhttpx.AsyncClient(timeout=None)\nwith httpx.Client() as client:\n    client.get('https://gmail.com')\nwith httpx.Client(timeout=None) as client:\n    client.get('https://gmail.com')\nasync with httpx.AsyncClient() as client:\n    await client.get('https://gmail.com')\nasync with httpx.AsyncClient(timeout=None) as client:\n    await client.get('https://gmail.com')\n\n# Okay\nnot_requests.get('https://gmail.com')\nrequests.get('https://gmail.com', timeout=5)\nrequests.post('https://gmail.com', timeout=5)\nrequests.put('https://gmail.com', timeout=5)\nrequests.delete('https://gmail.com', timeout=5)\nrequests.patch('https://gmail.com', timeout=5)\nrequests.options('https://gmail.com', timeout=5)\nrequests.head('https://gmail.com', timeout=5)\nhttpx.get('https://gmail.com', timeout=5)\nhttpx.post('https://gmail.com', timeout=5)\nhttpx.put('https://gmail.com', timeout=5)\nhttpx.delete('https://gmail.com', timeout=5)\nhttpx.patch('https://gmail.com', timeout=5)\nhttpx.options('https://gmail.com', timeout=5)\nhttpx.head('https://gmail.com', timeout=5)\nhttpx.Client(timeout=5)\nhttpx.AsyncClient(timeout=5)\nwith httpx.Client(timeout=5) as client:\n    client.get('https://gmail.com')\nasync with httpx.AsyncClient(timeout=5) as client:\n    await client.get('https://gmail.com')\n"
  },
  {
    "path": "examples/requests-ssl-verify-disabled.py",
    "content": "import httpx\nimport requests\n\n# Errors\nrequests.get('https://gmail.com', timeout=30, verify=True)\nrequests.get('https://gmail.com', timeout=30, verify=False)\nrequests.post('https://gmail.com', timeout=30, verify=True)\nrequests.post('https://gmail.com', timeout=30, verify=False)\nrequests.put('https://gmail.com', timeout=30, verify=True)\nrequests.put('https://gmail.com', timeout=30, verify=False)\nrequests.delete('https://gmail.com', timeout=30, verify=True)\nrequests.delete('https://gmail.com', timeout=30, verify=False)\nrequests.patch('https://gmail.com', timeout=30, verify=True)\nrequests.patch('https://gmail.com', timeout=30, verify=False)\nrequests.options('https://gmail.com', timeout=30, verify=True)\nrequests.options('https://gmail.com', timeout=30, verify=False)\nrequests.head('https://gmail.com', timeout=30, verify=True)\nrequests.head('https://gmail.com', timeout=30, verify=False)\n\n# Okay\nhttpx.request('GET', 'https://gmail.com', timeout=30, verify=True)\nhttpx.request('GET', 'https://gmail.com', timeout=30, verify=False)\nhttpx.get('https://gmail.com', timeout=30, verify=True)\nhttpx.get('https://gmail.com', timeout=30, verify=False)\nhttpx.options('https://gmail.com', timeout=30, verify=True)\nhttpx.options('https://gmail.com', timeout=30, verify=False)\nhttpx.head('https://gmail.com', timeout=30, verify=True)\nhttpx.head('https://gmail.com', timeout=30, verify=False)\nhttpx.post('https://gmail.com', timeout=30, verify=True)\nhttpx.post('https://gmail.com', timeout=30, verify=False)\nhttpx.put('https://gmail.com', timeout=30, verify=True)\nhttpx.put('https://gmail.com', timeout=30, verify=False)\nhttpx.patch('https://gmail.com', timeout=30, verify=True)\nhttpx.patch('https://gmail.com', timeout=30, verify=False)\nhttpx.delete('https://gmail.com', timeout=30, verify=True)\nhttpx.delete('https://gmail.com', timeout=30, verify=False)\nhttpx.stream('https://gmail.com', timeout=30, verify=True)\nhttpx.stream('https://gmail.com', timeout=30, verify=False)\nhttpx.Client(timeout=30)\nhttpx.Client(timeout=30, verify=False)\nhttpx.AsyncClient(timeout=30)\nhttpx.AsyncClient(timeout=30, verify=False)\n"
  },
  {
    "path": "examples/shelve_open.py",
    "content": "import os\nimport shelve\nimport tempfile\n\nwith tempfile.TemporaryDirectory() as d:\n    filename = os.path.join(d, 'shelf')\n\n    with shelve.open(filename) as db:\n        db['spam'] = {'eggs': 'ham'}\n\n    with shelve.open(filename) as db:\n        print(db['spam'])\n"
  },
  {
    "path": "examples/skip.py",
    "content": "subprocess.call([\"/bin/ls\", \"-l\"])\nsubprocess.call([\"/bin/ls\", \"-l\"]) #noqa\nsubprocess.call([\"/bin/ls\", \"-l\"]) # noqa\nsubprocess.call([\"/bin/ls\", \"-l\"]) # nosec\nsubprocess.call([\"/bin/ls\", \"-l\"])\nsubprocess.call([\"/bin/ls\", \"-l\"]) #nosec\nsubprocess.call([\"/bin/ls\", \"-l\"])\n"
  },
  {
    "path": "examples/snmp.py",
    "content": "from pysnmp.hlapi import CommunityData, UsmUserData\n\n# SHOULD FAIL\na = CommunityData('public', mpModel=0)\n# SHOULD FAIL\ninsecure = UsmUserData(\"securityName\")\n# SHOULD FAIL\nauth_no_priv = UsmUserData(\"securityName\",\"authName\")\n# SHOULD PASS\nless_insecure = UsmUserData(\"securityName\",\"authName\",\"privName\")\n"
  },
  {
    "path": "examples/sql_multiline_statements.py",
    "content": "import sqlalchemy\n\n# bad\nquery = \"\"\"SELECT *\nFROM foo WHERE id = '%s'\"\"\" % identifier\nquery = \"\"\"INSERT INTO foo\nVALUES ('a', 'b', '%s')\"\"\" % value\nquery = \"\"\"DELETE FROM foo\nWHERE id = '%s'\"\"\" % identifier\nquery = \"\"\"UPDATE foo\nSET value = 'b'\nWHERE id = '%s'\"\"\" % identifier\nquery = \"\"\"WITH cte AS (SELECT x FROM foo)\nSELECT x FROM cte WHERE x = '%s'\"\"\" % identifier\n# bad alternate forms\nquery = \"\"\"SELECT *\nFROM foo\nWHERE id = '\"\"\" + identifier + \"'\"\nquery = \"\"\"SELECT *\nFROM foo\nWHERE id = '{}'\"\"\".format(identifier)\n\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\n\"\"\"\n\n# bad\ncur.execute(\"\"\"SELECT *\nFROM foo\nWHERE id = '%s'\"\"\" % identifier)\ncur.execute(\"\"\"INSERT INTO foo\nVALUES ('a', 'b', '%s')\"\"\" % value)\ncur.execute(\"\"\"DELETE FROM foo\nWHERE id = '%s'\"\"\" % identifier)\ncur.execute(\"\"\"UPDATE foo\nSET value = 'b'\nWHERE id = '%s'\"\"\" % identifier)\n# bad alternate forms\ncur.execute(\"\"\"SELECT *\nFROM foo\nWHERE id = '\"\"\" + identifier + \"'\")\ncur.execute(\"\"\"SELECT *\nFROM foo\nWHERE id = '{}'\"\"\".format(identifier))\n\n# bad with f-string\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\n\"\"\"\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\n\"\"\"\n\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\"\"\"\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\"\"\"\n\ncur.execute(f\"\"\"\nSELECT\n    {column_name}\nFROM foo\nWHERE id = 1\"\"\")\n\ncur.execute(f\"\"\"\nSELECT\n    {a + b}\nFROM foo\nWHERE id = 1\"\"\")\n\ncur.execute(f\"\"\"\nINSERT INTO\n    {table_name}\nVALUES (1)\"\"\")\ncur.execute(f\"\"\"\nUPDATE {table_name}\nSET id = 1\"\"\")\n\n# implicit concatenation mixed with f-strings\ncur.execute(\"SELECT \"\n            f\"{column_name} \"\n            \"FROM foo \"\n            \"WHERE id = 1\"\n            )\ncur.execute(\"INSERT INTO \"\n            f\"{table_name} \"\n            \"VALUES (1)\")\ncur.execute(f\"UPDATE {table_name} \"\n            \"SET id = 1\")\n\n# good\ncur.execute(\"\"\"SELECT *\nFROM foo\nWHERE id = '%s'\"\"\", identifier)\ncur.execute(\"\"\"INSERT INTO foo\nVALUES ('a', 'b', '%s')\"\"\", value)\ncur.execute(\"\"\"DELETE FROM foo\nWHERE id = '%s'\"\"\", identifier)\ncur.execute(\"\"\"UPDATE foo\nSET value = 'b'\nWHERE id = '%s'\"\"\", identifier)\n\n\n# bug: https://bugs.launchpad.net/bandit/+bug/1479625\ndef a():\n    def b():\n        pass\n\n    return b\n\n\na()(\"\"\"SELECT %s\nFROM foo\"\"\" % val)\n\n# skip\nquery = \"\"\"SELECT *\nFROM foo WHERE id = '%s'\"\"\" % identifier  # nosec\nquery = \"\"\"SELECT *\nFROM foo WHERE id = '%s'\"\"\" % identifier  # nosec B608\nquery = \"\"\"\nSELECT *\nFROM foo\nWHERE id = '%s'\n\"\"\" % identifier  # nosec B608\n\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\n\"\"\"  # nosec\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\n\"\"\"  # nosec B608\n\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\"\"\"  # nosec\nquery = f\"\"\"\nSELECT *\nFROM foo\nWHERE id = {identifier}\"\"\"  # nosec B608\n\ncur.execute(\"SELECT * \"  # nosec\n            \"FROM foo \"\n            f\"WHERE id = {identifier}\")\ncur.execute(\"SELECT * \"  # nosec B608\n            \"FROM foo \"\n            f\"WHERE id = {identifier}\")\n\nquery = (\"SELECT * \"  # nosec\n         \"FROM foo \"\n         f\"WHERE id = {identifier}\")\nquery = (\"SELECT * \"  # nosec B608\n         \"FROM foo \"\n         f\"WHERE id = {identifier}\")\n\n# nosec is not recognized for the 4 below cases in python 3.7\nquery = (\"SELECT * \"\n         \"FROM foo \"  # nosec\n         f\"WHERE id = {identifier}\")\nquery = (\"SELECT * \"\n         \"FROM foo \"  # nosec B608\n         f\"WHERE id = {identifier}\")\nquery = (\"SELECT * \"\n         \"FROM foo \"\n         f\"WHERE id = {identifier}\")  # nosec\nquery = (\"SELECT * \"\n         \"FROM foo \"\n         f\"WHERE id = {identifier}\")  # nosec B608\n"
  },
  {
    "path": "examples/sql_statements.py",
    "content": "import sqlalchemy\n\n# bad\nquery = \"SELECT * FROM foo WHERE id = '%s'\" % identifier\nquery = \"INSERT INTO foo VALUES ('a', 'b', '%s')\" % value\nquery = \"INSERT INTO foo VALUES('a', 'b', '%s')\" % value\nquery = \"DELETE FROM foo WHERE id = '%s'\" % identifier\nquery = \"UPDATE foo SET value = 'b' WHERE id = '%s'\" % identifier\nquery = \"\"\"WITH cte AS (SELECT x FROM foo)\nSELECT x FROM cte WHERE x = '%s'\"\"\" % identifier\n# bad alternate forms\nquery = \"SELECT * FROM foo WHERE id = '\" + identifier + \"'\"\nquery = \"SELECT * FROM foo WHERE id = '{}'\".format(identifier)\nquery = \"SELECT * FROM foo WHERE id = '[VALUE]'\".replace(\"[VALUE]\", identifier)\n\n# bad\ncur.execute(\"SELECT * FROM foo WHERE id = '%s'\" % identifier)\ncur.execute(\"INSERT INTO foo VALUES ('a', 'b', '%s')\" % value)\ncur.execute(\"INSERT INTO foo VALUES('a', 'b', '%s')\" % value)\ncur.execute(\"DELETE FROM foo WHERE id = '%s'\" % identifier)\ncur.execute(\"UPDATE foo SET value = 'b' WHERE id = '%s'\" % identifier)\n# bad alternate forms\ncur.execute(\"SELECT * FROM foo WHERE id = '\" + identifier + \"'\")\ncur.execute(\"SELECT * FROM foo WHERE id = '{}'\".format(identifier))\ncur.execute(\"SELECT * FROM foo WHERE id = '[VALUE]'\".replace(\"[VALUE]\", identifier))\n\n# bad f-strings\ncur.execute(f\"SELECT {column_name} FROM foo WHERE id = 1\")\ncur.execute(f\"SELECT {a + b} FROM foo WHERE id = 1\")\ncur.execute(f\"INSERT INTO {table_name} VALUES (1)\")\ncur.execute(f\"INSERT INTO {table_name} VALUES(1)\")\ncur.execute(f\"UPDATE {table_name} SET id = 1\")\n\n# good\ncur.execute(\"SELECT * FROM foo WHERE id = '%s'\", identifier)\ncur.execute(\"INSERT INTO foo VALUES ('a', 'b', '%s')\", value)\ncur.execute(\"INSERT INTO foo VALUES('a', 'b', '%s')\", value)\ncur.execute(\"DELETE FROM foo WHERE id = '%s'\", identifier)\ncur.execute(\"UPDATE foo SET value = 'b' WHERE id = '%s'\", identifier)\n\n# bug: https://bugs.launchpad.net/bandit/+bug/1479625\ndef a():\n    def b():\n        pass\n    return b\n\na()(\"SELECT %s FROM foo\" % val)\n\n# real world false positives\nchoices=[('server_list', _(\"Select from active instances\"))]\nprint(\"delete from the cache as the first argument\")\n"
  },
  {
    "path": "examples/ssl-insecure-version.py",
    "content": "import ssl\nfrom pyOpenSSL import SSL\n\nssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv2)\nSSL.Context(method=SSL.SSLv2_METHOD)\nSSL.Context(method=SSL.SSLv23_METHOD)\n\nherp_derp(ssl_version=ssl.PROTOCOL_SSLv2)\nherp_derp(method=SSL.SSLv2_METHOD)\nherp_derp(method=SSL.SSLv23_METHOD)\n\n# strict tests\nssl.wrap_socket(ssl_version=ssl.PROTOCOL_SSLv3)\nssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1)\nSSL.Context(method=SSL.SSLv3_METHOD)\nSSL.Context(method=SSL.TLSv1_METHOD)\n\nherp_derp(ssl_version=ssl.PROTOCOL_SSLv3)\nherp_derp(ssl_version=ssl.PROTOCOL_TLSv1)\nherp_derp(method=SSL.SSLv3_METHOD)\nherp_derp(method=SSL.TLSv1_METHOD)\n\nssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLSv1_1)\nSSL.Context(method=SSL.TLSv1_1_METHOD)\n\nherp_derp(ssl_version=ssl.PROTOCOL_TLSv1_1)\nherp_derp(method=SSL.TLSv1_1_METHOD)\n\n\nssl.wrap_socket()\n\ndef open_ssl_socket(version=ssl.PROTOCOL_SSLv2):\n    pass\n\ndef open_ssl_socket(version=SSL.SSLv2_METHOD):\n    pass\n\ndef open_ssl_socket(version=SSL.SSLv23_METHOD):\n    pass\n\ndef open_ssl_socket(version=SSL.TLSv1_1_METHOD):\n    pass\n\n# this one will pass ok\ndef open_ssl_socket(version=SSL.TLSv1_2_METHOD):\n    pass\n"
  },
  {
    "path": "examples/subprocess_shell.py",
    "content": "import subprocess\nfrom subprocess import Popen as pop\n\n\ndef Popen(*args, **kwargs):\n    print('hi')\n\n    def __len__(self):\n        return 0\n\npop('/bin/gcc --version', shell=True)\nPopen('/bin/gcc --version', shell=True)\n\nsubprocess.Popen('/bin/gcc --version', shell=True)\nsubprocess.Popen(['/bin/gcc', '--version'], shell=False)\nsubprocess.Popen(['/bin/gcc', '--version'])\n\nsubprocess.call([\"/bin/ls\",\n                 \"-l\"\n                 ])\nsubprocess.call('/bin/ls -l', shell=True)\n\nsubprocess.check_call(['/bin/ls', '-l'], shell=False)\nsubprocess.check_call('/bin/ls -l', shell=True)\n\nsubprocess.check_output(['/bin/ls', '-l'])\nsubprocess.check_output('/bin/ls -l', shell=True)\nsubprocess.check_output([], stdout=None)\n\nsubprocess.getoutput('/bin/ls -l')\nsubprocess.getstatusoutput('/bin/ls -l')\n\nsubprocess.run(['/bin/ls', '-l'])\nsubprocess.run('/bin/ls -l', shell=True)\n\nsubprocess.Popen('/bin/ls *', shell=True)\nsubprocess.Popen('/bin/ls %s' % ('something',), shell=True)\nsubprocess.Popen('/bin/ls {}'.format('something'), shell=True)\n\ncommand = \"/bin/ls\" + unknown_function()\nsubprocess.Popen(command, shell=True)\n\nsubprocess.Popen('/bin/ls && cat /etc/passwd', shell=True)\n\ncommand = 'pwd'\nsubprocess.call(command, shell='True')\nsubprocess.call(command, shell='False')\nsubprocess.call(command, shell='None')\nsubprocess.call(command, shell=1)\n\nsubprocess.call(command, shell=Popen())\nsubprocess.call(command, shell=[True])\nsubprocess.call(command, shell={'IS': 'True'})\nsubprocess.call(command, shell=command)\n\nsubprocess.call(command, shell=False)\nsubprocess.call(command, shell=0)\nsubprocess.call(command, shell=[])\nsubprocess.call(command, shell={})\nsubprocess.call(command, shell=None)\n"
  },
  {
    "path": "examples/tarfile_extractall.py",
    "content": "import sys\nimport tarfile\nimport tempfile\n\n\ndef unsafe_archive_handler(filename):\n    tar = tarfile.open(filename)\n    tar.extractall(path=tempfile.mkdtemp())\n    tar.close()\n\n\ndef managed_members_archive_handler(filename):\n    tar = tarfile.open(filename)\n    tar.extractall(path=tempfile.mkdtemp(), members=members_filter(tar))\n    tar.close()\n\n\ndef filter_data_archive_handler(filename):\n    tar = tarfile.open(filename)\n    tar.extractall(path=tempfile.mkdtemp(), filter=\"data\")\n    tar.close()\n\n\ndef filter_fully_trusted_archive_handler(filename):\n    tar = tarfile.open(filename)\n    tar.extractall(path=tempfile.mkdtemp(), filter=\"fully_trusted\")\n    tar.close()\n\n\ndef list_members_archive_handler(filename):\n    tar = tarfile.open(filename)\n    tar.extractall(path=tempfile.mkdtemp(), members=[])\n    tar.close()\n\n\ndef provided_members_archive_handler(filename):\n    tar = tarfile.open(filename)\n    tarfile.extractall(path=tempfile.mkdtemp(), members=tar)\n    tar.close()\n\n\ndef members_filter(tarfile):\n    result = []\n    for member in tarfile.getmembers():\n        if '../' in member.name:\n            print('Member name container directory traversal sequence')\n            continue\n        elif (member.issym() or member.islnk()) and ('../' in member.linkname):\n            print('Symlink to external resource')\n            continue\n        result.append(member)\n    return result\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) > 1:\n        filename = sys.argv[1]\n        unsafe_archive_handler(filename)\n        managed_members_archive_handler(filename)\n        filter_data_archive_handler(filename)\n        filter_fully_trusted_archive_handler(filename)\n"
  },
  {
    "path": "examples/telnetlib.py",
    "content": "import telnetlib\nimport getpass\n\nhost = sys.argv[1]\n\nusername = raw_input('Username:')\npassword = getpass.getpass()\ntn = telnetlib.Telnet(host)\n\ntn.read_until(\"login: \")\ntn.write(username + \"\\n\")\nif password:\n    tn.read_until(\"Password: \")\n    tn.write(password + \"\\n\")\n\ntn.write(\"ls\\n\")\ntn.write(\"exit\\n\")\n\nprint(tn.read_all())\n"
  },
  {
    "path": "examples/trojansource.py",
    "content": "#!/usr/bin/env python3\n# cf. https://trojansource.codes/ & https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574\naccess_level = \"user\"\nif access_level != 'none‮⁦': # Check if admin ⁩⁦' and access_level != 'user\n    print(\"You are an admin.\\n\")\n"
  },
  {
    "path": "examples/trojansource_latin1.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: latin-1 -*-\n# cf. https://trojansource.codes & https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42574\n# Some special characters: \naccess_level = \"user\"\nif access_level != 'none??': # Check if admin ??' and access_level != 'user\n    print(\"You are an admin.\\n\")\n"
  },
  {
    "path": "examples/try_except_continue.py",
    "content": "# bad\nfor i in {0,1}:\n    try:\n        a = i\n    except:\n        continue\n\n\n# bad\nwhile keep_trying:\n    try:\n        a = 1\n    except Exception:\n        continue\n\n\n# bad\nfor i in {0,2}:\n    try:\n        a = i\n    except ZeroDivisionError:\n        continue\n    except:\n        a = 2\n\n\n# good\nwhile keep_trying:\n    try:\n        a = 1\n    except:\n        a = 2\n"
  },
  {
    "path": "examples/try_except_pass.py",
    "content": "# bad\ntry:\n    a = 1\nexcept:\n    pass\n\n\n# bad\ntry:\n    a = 1\nexcept Exception:\n    pass\n\n\n# bad\ntry:\n    a = 1\nexcept ZeroDivisionError:\n    pass\nexcept:\n    a = 2\n\n\n# good\ntry:\n    a = 1\nexcept:\n    a = 2\n\n\n# silly, but ok\ntry:\n    a = 1\nexcept:\n    pass\n    a = 2\n"
  },
  {
    "path": "examples/unverified_context.py",
    "content": "import ssl\n\n# Correct\ncontext = ssl.create_default_context()\n\n# Incorrect: unverified context\ncontext = ssl._create_unverified_context()\n"
  },
  {
    "path": "examples/urlopen.py",
    "content": "''' Example dangerous usage of urllib.request opener functions\n\nThe urllib.request opener functions and object can open http, ftp,\nand file urls. Often, the ability to open file urls is overlooked leading\nto code that can unexpectedly open files on the local server. This\ncould be used by an attacker to leak information about the server.\n'''\n\n# Python 3\nimport urllib.request\n\n# Six\nimport six\n\ndef test_urlopen():\n    # Python 3\n    urllib.request.urlopen('file:///bin/ls')\n    urllib.request.urlretrieve('file:///bin/ls', '/bin/ls2')\n    opener = urllib.request.URLopener()\n    opener.open('file:///bin/ls')\n    opener.retrieve('file:///bin/ls')\n    opener = urllib.request.FancyURLopener()\n    opener.open('file:///bin/ls')\n    opener.retrieve('file:///bin/ls')\n\n    # Six\n    six.moves.urllib.request.urlopen('file:///bin/ls')\n    six.moves.urllib.request.urlretrieve('file:///bin/ls', '/bin/ls2')\n    opener = six.moves.urllib.request.URLopener()\n    opener.open('file:///bin/ls')\n    opener.retrieve('file:///bin/ls')\n    opener = six.moves.urllib.request.FancyURLopener()\n    opener.open('file:///bin/ls')\n    opener.retrieve('file:///bin/ls')\n"
  },
  {
    "path": "examples/weak_cryptographic_key_sizes.py",
    "content": "from cryptography.hazmat import backends\nfrom cryptography.hazmat.primitives.asymmetric import dsa\nfrom cryptography.hazmat.primitives.asymmetric import ec\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom Crypto.PublicKey import DSA as pycrypto_dsa\nfrom Crypto.PublicKey import RSA as pycrypto_rsa\nfrom Cryptodome.PublicKey import DSA as pycryptodomex_dsa\nfrom Cryptodome.PublicKey import RSA as pycryptodomex_rsa\n\n\n# Correct\ndsa.generate_private_key(key_size=2048,\n                         backend=backends.default_backend())\nec.generate_private_key(curve=ec.SECP384R1,\n                        backend=backends.default_backend())\nrsa.generate_private_key(public_exponent=65537,\n                         key_size=2048,\n                         backend=backends.default_backend())\npycrypto_dsa.generate(bits=2048)\npycrypto_rsa.generate(bits=2048)\npycryptodomex_dsa.generate(bits=2048)\npycryptodomex_rsa.generate(bits=2048)\n\n# Also correct: without keyword args\ndsa.generate_private_key(4096,\n                         backends.default_backend())\nec.generate_private_key(ec.SECP256K1,\n                        backends.default_backend())\nrsa.generate_private_key(3,\n                         4096,\n                         backends.default_backend())\npycrypto_dsa.generate(4096)\npycrypto_rsa.generate(4096)\npycryptodomex_dsa.generate(4096)\npycryptodomex_rsa.generate(4096)\n\n# Incorrect: weak key sizes\ndsa.generate_private_key(key_size=1024,\n                         backend=backends.default_backend())\nec.generate_private_key(curve=ec.SECT163R2,\n                        backend=backends.default_backend())\nrsa.generate_private_key(public_exponent=65537,\n                         key_size=1024,\n                         backend=backends.default_backend())\npycrypto_dsa.generate(bits=1024)\npycrypto_rsa.generate(bits=1024)\npycryptodomex_dsa.generate(bits=1024)\npycryptodomex_rsa.generate(bits=1024)\n\n# Also incorrect: without keyword args\ndsa.generate_private_key(512,\n                         backends.default_backend())\nec.generate_private_key(ec.SECT163R2,\n                        backends.default_backend())\nrsa.generate_private_key(3,\n                         512,\n                         backends.default_backend())\npycrypto_dsa.generate(512)\npycrypto_rsa.generate(512)\npycryptodomex_dsa.generate(512)\npycryptodomex_rsa.generate(512)\n\n# Don't crash when the size is variable\nrsa.generate_private_key(public_exponent=65537,\n                         key_size=some_key_size,\n                         backend=backends.default_backend())\n\n# Can't reliably know which curve was passed, in some cases like below\nec.generate_private_key(\n    curve=curves[self.curve]['create'](self.size),\n    backend=backends.default_backend()\n)\n"
  },
  {
    "path": "examples/wildcard-injection.py",
    "content": "import os as o\nimport subprocess as subp\n\n# Vulnerable to wildcard injection\no.system(\"/bin/tar xvzf *\")\no.system('/bin/chown *')\no.popen2('/bin/chmod *')\nsubp.Popen('/bin/chown *', shell=True)\n\n# Not vulnerable to wildcard injection\nsubp.Popen('/bin/rsync *')\nsubp.Popen(\"/bin/chmod *\")\nsubp.Popen(['/bin/chown', '*'])\nsubp.Popen([\"/bin/chmod\", sys.argv[1], \"*\"],\n                 stdin=subprocess.PIPE, stdout=subprocess.PIPE)\no.spawnvp(os.P_WAIT, 'tar', ['tar', 'xvzf', '*'])\n"
  },
  {
    "path": "examples/xml_etree_celementtree.py",
    "content": "import xml.etree.cElementTree as badET\nimport defusedxml.cElementTree as goodET\n\nxmlString = \"<note>\\n<to>Tove</to>\\n<from>Jani</from>\\n<heading>Reminder</heading>\\n<body>Don't forget me this weekend!</body>\\n</note>\"\n\n# unsafe\ntree = badET.fromstring(xmlString)\nprint(tree)\nbadET.parse('filethatdoesntexist.xml')\nbadET.iterparse('filethatdoesntexist.xml')\na = badET.XMLParser()\n\n# safe\ntree = goodET.fromstring(xmlString)\nprint(tree)\ngoodET.parse('filethatdoesntexist.xml')\ngoodET.iterparse('filethatdoesntexist.xml')\na = goodET.XMLParser()\n"
  },
  {
    "path": "examples/xml_etree_elementtree.py",
    "content": "import xml.etree.ElementTree as badET\nimport defusedxml.ElementTree as goodET\n\nxmlString = \"<note>\\n<to>Tove</to>\\n<from>Jani</from>\\n<heading>Reminder</heading>\\n<body>Don't forget me this weekend!</body>\\n</note>\"\n\n# unsafe\ntree = badET.fromstring(xmlString)\nprint(tree)\nbadET.parse('filethatdoesntexist.xml')\nbadET.iterparse('filethatdoesntexist.xml')\na = badET.XMLParser()\n\n# safe\ntree = goodET.fromstring(xmlString)\nprint(tree)\ngoodET.parse('filethatdoesntexist.xml')\ngoodET.iterparse('filethatdoesntexist.xml')\na = goodET.XMLParser()\n"
  },
  {
    "path": "examples/xml_expatbuilder.py",
    "content": "import xml.dom.expatbuilder as bad\nimport defusedxml.expatbuilder as good\n\nbad.parse('filethatdoesntexist.xml')\ngood.parse('filethatdoesntexist.xml')\n\nxmlString = \"<note>\\n<to>Tove</to>\\n<from>Jani</from>\\n<heading>Reminder</heading>\\n<body>Don't forget me this weekend!</body>\\n</note>\"\n\nbad.parseString(xmlString)\ngood.parseString(xmlString)\n"
  },
  {
    "path": "examples/xml_expatreader.py",
    "content": "import xml.sax.expatreader as bad\nimport defusedxml.expatreader as good\n\np = bad.create_parser()\nb = good.create_parser()\n"
  },
  {
    "path": "examples/xml_minidom.py",
    "content": "from xml.dom.minidom import parseString as badParseString\nfrom defusedxml.minidom import parseString as goodParseString\na = badParseString(\"<myxml>Some data some more data</myxml>\")\nprint(a)\nb = goodParseString(\"<myxml>Some data some more data</myxml>\")\nprint(b)\n\n\nfrom xml.dom.minidom import parse as badParse\nfrom defusedxml.minidom import parse as goodParse\na = badParse(\"somfilethatdoesntexist.xml\")\nprint(a)\nb = goodParse(\"somefilethatdoesntexist.xml\")\nprint(b)\n"
  },
  {
    "path": "examples/xml_pulldom.py",
    "content": "from xml.dom.pulldom import parseString as badParseString\nfrom defusedxml.pulldom import parseString as goodParseString\na = badParseString(\"<myxml>Some data some more data</myxml>\")\nprint(a)\nb = goodParseString(\"<myxml>Some data some more data</myxml>\")\nprint(b)\n\n\nfrom xml.dom.pulldom import parse as badParse\nfrom defusedxml.pulldom import parse as goodParse\na = badParse(\"somfilethatdoesntexist.xml\")\nprint(a)\nb = goodParse(\"somefilethatdoesntexist.xml\")\nprint(b)\n"
  },
  {
    "path": "examples/xml_sax.py",
    "content": "import xml.sax\nfrom xml import sax\nimport defusedxml.sax\n\nclass ExampleContentHandler(xml.sax.ContentHandler):\n    def __init__(self):\n        xml.sax.ContentHandler.__init__(self)\n\n    def startElement(self, name, attrs):\n        print('start:', name)\n\n    def endElement(self, name):\n        print('end:', name)\n\n    def characters(self, content):\n        print('chars:', content)\n\ndef main():\n    xmlString = \"<note>\\n<to>Tove</to>\\n<from>Jani</from>\\n<heading>Reminder</heading>\\n<body>Don't forget me this weekend!</body>\\n</note>\"\n    # bad\n    xml.sax.parseString(xmlString, ExampleContentHandler())\n    xml.sax.parse('notaxmlfilethatexists.xml', ExampleContentHandler())\n    sax.parseString(xmlString, ExampleContentHandler())\n    sax.parse('notaxmlfilethatexists.xml', ExampleContentHandler)\n\n    # good\n    defusedxml.sax.parseString(xmlString, ExampleContentHandler())\n\n    # bad\n    xml.sax.make_parser()\n    sax.make_parser()\n    print('nothing')\n    # good\n    defusedxml.sax.make_parser()\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "examples/xml_xmlrpc.py",
    "content": "import xmlrpc\nfrom SimpleXMLRPCServer import SimpleXMLRPCServer\n\ndef is_even(n):\n    return n%2 == 0\n\nserver = SimpleXMLRPCServer((\"localhost\", 8000))\nprint(\"Listening on port 8000...\")\nserver.register_function(is_even, \"is_even\")\nserver.serve_forever()\n"
  },
  {
    "path": "examples/yaml_load.py",
    "content": "import json\nimport yaml\nfrom yaml import CSafeLoader\nfrom yaml import SafeLoader\n\n\ndef test_yaml_load():\n    ystr = yaml.dump({'a': 1, 'b': 2, 'c': 3})\n    y = yaml.load(ystr)\n    yaml.dump(y)\n    try:\n        y = yaml.load(ystr, Loader=yaml.CSafeLoader)\n    except AttributeError:\n        # CSafeLoader only exists if you build yaml with LibYAML\n        y = yaml.load(ystr, Loader=yaml.SafeLoader)\n\n\ndef test_json_load():\n    # no issue should be found\n    j = json.load(\"{}\")\n\nyaml.load(\"{}\", Loader=yaml.Loader)\n\n# no issue should be found\nyaml.load(\"{}\", SafeLoader)\nyaml.load(\"{}\", yaml.SafeLoader)\nyaml.load(\"{}\", CSafeLoader)\nyaml.load(\"{}\", yaml.CSafeLoader)\n"
  },
  {
    "path": "funding.json",
    "content": "{\n    \"version\": \"v1.0.0\",\n    \"entity\": {\n        \"type\": \"individual\",\n        \"role\": \"maintainer\",\n        \"name\": \"Eric Brown\",\n        \"email\": \"eric_wade_brown@yahoo.com\",\n        \"phone\": \"\",\n        \"description\": \"I’m passionate about developing tools that empower engineers to produce secure, hardened code, reducing vulnerabilities and strengthening software integrity. With a focus on security automation, I aim to make secure coding practices more accessible and integrated into development workflows.\",\n        \"webpageUrl\": {\n            \"url\": \"https://github.com\"\n        }\n    },\n    \"projects\": [{\n        \"guid\": \"bandit\",\n        \"name\": \"Bandit\",\n        \"description\": \" Bandit is a tool designed to find common security issues in Python code.\",\n        \"webpageUrl\": {\n            \"url\": \"https://github.com/PyCQA/bandit\"\n        },\n        \"repositoryUrl\": {\n            \"url\": \"https://github.com/PyCQA/bandit\"\n        },\n        \"licenses\": [\"spdx:Apache-2.0\"],\n        \"tags\": [\"python\", \"static-code-analysis\", \"security\", \"security-tools\"]\n    }],\n    \"funding\": {\n        \"channels\": [\n            {\n                \"guid\": \"github\",\n                \"type\": \"payment-provider\",\n                \"address\": \"https://github.com/sponsors/ericwb\",\n                \"description\": \"Pay with your credit card through this gateway and setup recurring subscriptions.\"\n            },\n            {\n                \"guid\": \"psf\",\n                \"type\": \"payment-provider\",\n                \"address\": \"https://psfmember.org/civicrm/contribute/transact/?reset=1&id=42\",\n                \"description\": \"Pay with your credit card through this gateway and setup recurring subscriptions.\"\n            }\n        ],\n        \"plans\": [\n            {\n                \"guid\": \"developer-time\",\n                \"status\": \"active\",\n                \"name\": \"Developer compensation\",\n                \"description\": \"This will cover the cost of one developer working part-time on the projects.\",\n                \"amount\": 1000,\n                \"currency\": \"USD\",\n                \"frequency\": \"monthly\",\n                \"channels\": [\"github\", \"psf\"]\n            },\n            {\n                \"guid\": \"angel-plan\",\n                \"status\": \"active\",\n                \"name\": \"Goodwill plan\",\n                \"description\": \"Pay anything you wish to show your goodwill for the project.\",\n                \"amount\": 0,\n                \"currency\": \"USD\",\n                \"frequency\": \"one-time\",\n                \"channels\": [\"psf\"]\n            }\n        ]\n    }\n}\n"
  },
  {
    "path": "pylintrc",
    "content": "# The format of this file isn't really documented; just use --generate-rcfile\n\n[Messages Control]\n# C0111: Don't require docstrings on every method\n# C0301: Handled by pep8\n# C0325: Parens are required on print in py3x\n# F0401: Imports are check by other linters\n# W0511: TODOs in code comments are fine.\n# W0622: Redefining id is fine.\n\n# TODO(browne): fix these in the future\n# C0103: invalid-name\n# C0114: Missing module docstring\n# C0115: Missing class docstring\n# C0116: Missing function or method docstring\n# C0201: consider-iterating-dictionary\n# C0206: Consider iterating with .items()\n# C0209: Foramtting a regular string which could be an f-string\n# C0413: wrong-import-position\n# C0415: Import outside toplevel\n# C1802: Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty\n# E0611: No name in module\n# E1101: no-member\n# R0801: Similar lines in 2 files\n# R0902: too-many-instance-attributes\n# R0912: too-many-branches\n# R0913: too-many-arguments\n# R0914: too-many-locals\n# R0915: too-many-statements\n# R1702: too-many-nested-blocks\n# R1705: no-else-return\n# R1710: inconsistent-return-statements\n# R1714: Consider merging these comparisons with 'in'\n# R1721: Unnecessary use of a comprehension\n# R1732: Consider using 'with' for resource-allocating operations\n# R1734: Consider using [] instead of list()\n# R1735: use-dict-literal\n# W0105: String statement has no effect\n# W0201: attribute-defined-outside-init\n# W0212: protected-access\n# W0246: Useless parent or super() delegation\n# W0603: global-statement\n# W0612: Unused variable\n# W0613: unused-argument\n# W0621: redefined-outer-name\n# W0707: Consider explicitly re-raising\n# W0718: Catching too general exception Exception\n# W1201: logging-not-lazy\n# W1203: Use lazy % or % formatting in logging functions\n# W1404: Implicit string concatenation found in call\n# W1514: Using open without explicitly specifying an encoding\ndisable=C0103,C0114,C0115,C0116,C0201,C0206,C0209,C0301,C0413,C0415,C1802,F0401,W0511,W0622,E0611,E1101,R0801,R0902,R0912,R0913,R0914,R0915,R1702,R1705,R1710,R1714,R1721,R1732,R1734,R1735,W0105,W0201,W0212,W0246,W0603,W0612,W0613,W0621,W0707,W0718,W1201,W1203,W1404,W1514\n\n[Basic]\n# Variable names can be 1 to 31 characters long, with lowercase and underscores\nvariable-rgx=[a-z_][a-z0-9_]{0,30}$\n\n# Argument names can be 2 to 31 characters long, with lowercase and underscores\nargument-rgx=[a-z_][a-z0-9_]{1,30}$\n\n# Method names should be at least 3 characters long\n# and be lowecased with underscores\nmethod-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$\n\n# Module names matching manila-* are ok (files in bin/)\nmodule-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(manila-[a-z0-9_-]+))$\n\n# Don't require docstrings on tests.\nno-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$\n\n[Design]\nmax-public-methods=100\nmin-public-methods=0\nmax-args=6\n\n[Variables]\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid to define new builtins when possible.\n# _ is used by our localization\nadditional-builtins=_\n\n[Similarities]\n# Minimum lines number of a similarity.\nmin-similarity-lines=10\n\n# Ignore comments when computing similarities.\nignore-comments=yes\n\n# We don't need to do pylint on the examples, too many false positives\nignore-paths=examples\n\n# Ignore docstrings when computing similarities.\nignore-docstrings=yes\n\n# Ignore imports when computing similarities.\nignore-imports=yes\n"
  },
  {
    "path": "requirements.txt",
    "content": "# The order of packages is significant, because pip processes them in the order\n# of appearance. Changing the order has an impact on the overall integration\n# process, which may cause wedges in the gate later.\nPyYAML>=5.3.1 # MIT\nstevedore>=1.20.0 # Apache-2.0\ncolorama>=0.3.9;platform_system==\"Windows\" # BSD License (3 clause)\nrich # MIT\n"
  },
  {
    "path": "scripts/main.py",
    "content": "#!/usr/bin/env python\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nfrom bandit import bandit\n\nif __name__ == \"__main__\":\n    bandit.main()\n"
  },
  {
    "path": "setup.cfg",
    "content": "[metadata]\nname = bandit\nsummary = Security oriented static analyser for python code.\ndescription_file =\n    README.rst\nauthor = PyCQA\nauthor_email = code-quality@python.org\nhome_page = https://bandit.readthedocs.io/\nlicense = Apache-2.0\nclassifiers =\n    Development Status :: 5 - Production/Stable\n    Environment :: Console\n    Intended Audience :: Information Technology\n    Intended Audience :: System Administrators\n    Intended Audience :: Developers\n    Operating System :: POSIX :: Linux\n    Operating System :: MacOS :: MacOS X\n    Programming Language :: Python\n    Programming Language :: Python :: 3\n    Programming Language :: Python :: 3.10\n    Programming Language :: Python :: 3.11\n    Programming Language :: Python :: 3.12\n    Programming Language :: Python :: 3.13\n    Programming Language :: Python :: 3.14\n    Programming Language :: Python :: 3 :: Only\n    Topic :: Security\nproject_urls =\n    Documentation = https://bandit.readthedocs.io/\n    Release Notes = https://github.com/PyCQA/bandit/releases\n    Source Code = https://github.com/PyCQA/bandit\n    Issue Tracker = https://github.com/PyCQA/bandit/issues\n    Discord = https://discord.gg/qYxpadCgkx\n    Sponsor = https://psfmember.org/civicrm/contribute/transact/?reset=1&id=42\n\n[extras]\nyaml =\n    PyYAML\ntoml =\n    tomli>=1.1.0; python_version < \"3.11\"\nbaseline =\n    GitPython>=3.1.30\nsarif =\n    sarif-om>=1.0.4\n    jschema-to-python>=1.2.3\n\n[entry_points]\nconsole_scripts =\n    bandit = bandit.cli.main:main\n    bandit-config-generator = bandit.cli.config_generator:main\n    bandit-baseline = bandit.cli.baseline:main\nbandit.blacklists =\n    calls = bandit.blacklists.calls:gen_blacklist\n    imports = bandit.blacklists.imports:gen_blacklist\nbandit.formatters =\n    csv = bandit.formatters.csv:report\n    json = bandit.formatters.json:report\n    txt = bandit.formatters.text:report\n    xml = bandit.formatters.xml:report\n    html = bandit.formatters.html:report\n    sarif = bandit.formatters.sarif:report\n    screen = bandit.formatters.screen:report\n    yaml = bandit.formatters.yaml:report\n    custom = bandit.formatters.custom:report\nbandit.plugins =\n    # bandit/plugins/app_debug.py\n    flask_debug_true = bandit.plugins.app_debug:flask_debug_true\n\n    # bandit/plugins/asserts.py\n    assert_used = bandit.plugins.asserts:assert_used\n\n    # bandit/plugins/crypto_request_no_cert_validation.py\n    request_with_no_cert_validation = bandit.plugins.crypto_request_no_cert_validation:request_with_no_cert_validation\n\n    # bandit/plugins/request_without_timeout.py\n    request_without_timeout = bandit.plugins.request_without_timeout:request_without_timeout\n\n    # bandit/plugins/exec.py\n    exec_used = bandit.plugins.exec:exec_used\n\n    # bandit/plugins/general_bad_File_permissions.py\n    set_bad_file_permissions = bandit.plugins.general_bad_file_permissions:set_bad_file_permissions\n\n    # bandit/plugins/general_bind_all_interfaces.py\n    hardcoded_bind_all_interfaces = bandit.plugins.general_bind_all_interfaces:hardcoded_bind_all_interfaces\n\n    # bandit/plugins/general_hardcoded_password.py\n    hardcoded_password_string = bandit.plugins.general_hardcoded_password:hardcoded_password_string\n    hardcoded_password_funcarg = bandit.plugins.general_hardcoded_password:hardcoded_password_funcarg\n    hardcoded_password_default = bandit.plugins.general_hardcoded_password:hardcoded_password_default\n\n    # bandit/plugins/general_hardcoded_tmp.py\n    hardcoded_tmp_directory = bandit.plugins.general_hardcoded_tmp:hardcoded_tmp_directory\n\n    # bandit/plugins/injection_paramiko.py\n    paramiko_calls = bandit.plugins.injection_paramiko:paramiko_calls\n\n    # bandit/plugins/injection_shell.py\n    subprocess_popen_with_shell_equals_true = bandit.plugins.injection_shell:subprocess_popen_with_shell_equals_true\n    subprocess_without_shell_equals_true = bandit.plugins.injection_shell:subprocess_without_shell_equals_true\n    any_other_function_with_shell_equals_true = bandit.plugins.injection_shell:any_other_function_with_shell_equals_true\n    start_process_with_a_shell = bandit.plugins.injection_shell:start_process_with_a_shell\n    start_process_with_no_shell = bandit.plugins.injection_shell:start_process_with_no_shell\n    start_process_with_partial_path = bandit.plugins.injection_shell:start_process_with_partial_path\n\n    # bandit/plugins/injection_sql.py\n    hardcoded_sql_expressions = bandit.plugins.injection_sql:hardcoded_sql_expressions\n\n    # bandit/plugins/hashlib_insecure_functions.py\n    hashlib_insecure_functions = bandit.plugins.hashlib_insecure_functions:hashlib\n\n    # bandit/plugins/injection_wildcard.py\n    linux_commands_wildcard_injection = bandit.plugins.injection_wildcard:linux_commands_wildcard_injection\n\n    # bandit/plugins/django_sql_injection.py\n    django_extra_used = bandit.plugins.django_sql_injection:django_extra_used\n    django_rawsql_used = bandit.plugins.django_sql_injection:django_rawsql_used\n\n    # bandit/plugins/insecure_ssl_tls.py\n    ssl_with_bad_version = bandit.plugins.insecure_ssl_tls:ssl_with_bad_version\n    ssl_with_bad_defaults = bandit.plugins.insecure_ssl_tls:ssl_with_bad_defaults\n    ssl_with_no_version = bandit.plugins.insecure_ssl_tls:ssl_with_no_version\n\n    # bandit/plugins/jinja2_templates.py\n    jinja2_autoescape_false = bandit.plugins.jinja2_templates:jinja2_autoescape_false\n\n    # bandit/plugins/mako_templates.py\n    use_of_mako_templates = bandit.plugins.mako_templates:use_of_mako_templates\n\n    # bandit/plugins/django_xss.py\n    django_mark_safe = bandit.plugins.django_xss:django_mark_safe\n\n    # bandit/plugins/try_except_continue.py\n    try_except_continue = bandit.plugins.try_except_continue:try_except_continue\n\n    # bandit/plugins/try_except_pass.py\n    try_except_pass = bandit.plugins.try_except_pass:try_except_pass\n\n    # bandit/plugins/weak_cryptographic_key.py\n    weak_cryptographic_key = bandit.plugins.weak_cryptographic_key:weak_cryptographic_key\n\n    # bandit/plugins/yaml_load.py\n    yaml_load = bandit.plugins.yaml_load:yaml_load\n\n    # bandit/plugins/ssh_no_host_key_verification.py\n    ssh_no_host_key_verification = bandit.plugins.ssh_no_host_key_verification:ssh_no_host_key_verification\n\n    # bandit/plugins/snmp_security_check.py\n    snmp_insecure_version = bandit.plugins.snmp_security_check:snmp_insecure_version_check\n    snmp_weak_cryptography = bandit.plugins.snmp_security_check:snmp_crypto_check\n\n    # bandit/plugins/logging_config_insecure_listen.py\n    logging_config_insecure_listen = bandit.plugins.logging_config_insecure_listen:logging_config_insecure_listen\n\n    #bandit/plugins/tarfile_unsafe_members.py\n    tarfile_unsafe_members = bandit.plugins.tarfile_unsafe_members:tarfile_unsafe_members\n\n    #bandit/plugins/pytorch_load.py\n    pytorch_load = bandit.plugins.pytorch_load:pytorch_load\n\n    # bandit/plugins/trojansource.py\n    trojansource = bandit.plugins.trojansource:trojansource\n\n    # bandit/plugins/markupsafe_markup_xss.py\n    markupsafe_markup_xss = bandit.plugins.markupsafe_markup_xss:markupsafe_markup_xss\n\n    # bandit/plugins/huggingface_unsafe_download.py\n    huggingface_unsafe_download = bandit.plugins.huggingface_unsafe_download:huggingface_unsafe_download\n\n[build_sphinx]\nall_files = 1\nbuild-dir = doc/build\nsource-dir = doc/source\n\n[pbr]\nautodoc_tree_index_modules = True\nautodoc_tree_excludes =\n  examples*\n"
  },
  {
    "path": "setup.py",
    "content": "# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\n\nimport setuptools\n\n\ndata_files = []\nman_path = \"doc/build/man/bandit.1\"\nif os.path.isfile(man_path):\n    data_files.append((\"share/man/man1\", [man_path]))\n\n\nsetuptools.setup(\n    python_requires=\">=3.10\",\n    setup_requires=[\"pbr>=2.0.0\"],\n    pbr=True,\n    data_files=data_files,\n)\n"
  },
  {
    "path": "test-requirements.txt",
    "content": "# The order of packages is significant, because pip processes them in the order\n# of appearance. Changing the order has an impact on the overall integration\n# process, which may cause wedges in the gate later.\ncoverage>=4.5.4 # Apache-2.0\nfixtures>=3.0.0 # Apache-2.0/BSD\nflake8>=4.0.0 # Apache-2.0\nstestr>=2.5.0 # Apache-2.0\ntestscenarios>=0.5.0 # Apache-2.0/BSD\ntesttools>=2.3.0 # MIT\nbeautifulsoup4>=4.8.0 # MIT\npylint==1.9.4 # GPLv2\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/functional/__init__.py",
    "content": ""
  },
  {
    "path": "tests/functional/test_baseline.py",
    "content": "# Copyright 2016 IBM Corp.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\nimport shutil\nimport subprocess\n\nimport fixtures\nimport testtools\n\nnew_candidates_all_total_lines = \"Total lines of code: 12\"\nnew_candidates_some_total_lines = \"Total lines of code: 9\"\nnew_candidates_no_nosec_lines = \"Total lines skipped (#nosec): 0\"\nnew_candidates_skip_nosec_lines = \"Total lines skipped (#nosec): 3\"\nbaseline_no_skipped_files = \"Files skipped (0):\"\nbaseline_no_issues_found = \"No issues identified.\"\nxml_sax_issue_id = \"Issue: [B317:blacklist]\"\nyaml_load_issue_id = \"Issue: [B506:yaml_load]\"\nshell_issue_id = \"Issue: [B602:subprocess_popen_with_shell_equals_true]\"\ncandidate_example_one = \"subprocess.Popen('/bin/ls *', shell=True)\"\ncandidate_example_two = \"subprocess.Popen('/bin/ls *', shell=True) # nosec\"\ncandidate_example_three = \"y = yaml.load(temp_str)\"\ncandidate_example_four = \"y = yaml.load(temp_str) # nosec\"\ncandidate_example_five = \"xml.sax.make_parser()\"\ncandidate_example_six = \"xml.sax.make_parser() # nosec\"\n\n\nclass BaselineFunctionalTests(testtools.TestCase):\n    \"\"\"Functional tests for Bandit baseline.\n\n    This set of tests is used to verify that the baseline comparison handles\n    finding and comparing results appropriately. The only comparison is the\n    number of candidates per file, meaning that any candidates found may\n    already exist in the baseline. In this case, all candidates are flagged\n    and a user will need to investigate the candidates related to that file.\n    \"\"\"\n\n    def setUp(self):\n        super().setUp()\n        self.examples_path = \"examples\"\n        self.baseline_commands = [\"bandit\", \"-r\"]\n        self.baseline_report_file = \"baseline_report.json\"\n\n    def _run_bandit_baseline(self, target_directory, baseline_file):\n        \"\"\"A helper method to run bandit baseline\n\n        This method will run the bandit baseline test provided an existing\n        baseline report and the target directory containing the content to be\n        tested.\n        :param target_directory: Directory containing content to be compared\n        :param baseline_file: File containing an existing baseline report\n        :return The baseline test results and return code\n        \"\"\"\n        cmds = self.baseline_commands + [\"-b\", baseline_file, target_directory]\n        process = subprocess.Popen(\n            cmds,\n            stdin=subprocess.PIPE,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            close_fds=True,\n        )\n        stdout, stderr = process.communicate()\n        return (stdout.decode(\"utf-8\"), process.poll())\n\n    def _create_baseline(self, baseline_paired_files):\n        \"\"\"A helper method to create a baseline to use during baseline test\n\n        This method will run bandit to create an initial baseline that can\n        then be used during the bandit baseline test. Since the file contents\n        of the baseline report can be extremely dynamic and difficult to create\n        ahead of time, we do this at runtime to reduce the risk of missing\n        something. To do this, we must temporary replace the file contents\n        with different code which will produce the proper baseline results to\n        be used during the baseline test.\n        :param baseline_paired_files A dictionary based set of files for which\n        to create the baseline report with. For each key file, a value file\n        is provided, which contains content to use in place of the key file\n        when the baseline report is created initially.\n        :return The target directory for the baseline test and the return code\n        of the bandit run to help determine whether the baseline report was\n        populated\n        \"\"\"\n        target_directory = self.useFixture(fixtures.TempDir()).path\n        baseline_results = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        for key_file, value_file in baseline_paired_files.items():\n            shutil.copy(\n                os.path.join(self.examples_path, value_file),\n                os.path.join(target_directory, key_file),\n            )\n        cmds = self.baseline_commands + [\n            \"-f\",\n            \"json\",\n            \"-o\",\n            baseline_results,\n            target_directory,\n        ]\n        process = subprocess.Popen(\n            cmds,\n            stdin=subprocess.PIPE,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.PIPE,\n            close_fds=True,\n        )\n        stdout, stderr = process.communicate()\n        return_code = process.poll()\n        for key_file, value_file in baseline_paired_files.items():\n            shutil.copy(\n                os.path.join(self.examples_path, key_file),\n                os.path.join(target_directory, key_file),\n            )\n        return (target_directory, return_code)\n\n    def test_no_new_candidates(self):\n        \"\"\"Tests when there are no new candidates\n\n        Test that bandit returns no issues found, as there are no new\n        candidates found compared with those found from the baseline.\n        \"\"\"\n        baseline_report_files = {\n            \"new_candidates-all.py\": \"new_candidates-all.py\"\n        }\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found results\n        self.assertEqual(1, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were no results (no candidates found)\n        self.assertEqual(0, return_code)\n        self.assertIn(new_candidates_all_total_lines, return_value)\n        self.assertIn(new_candidates_skip_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(baseline_no_issues_found, return_value)\n\n    def test_no_existing_no_new_candidates(self):\n        \"\"\"Tests when there are no new or existing candidates\n\n        Test file with no existing candidates from baseline and no new\n        candidates.\n        \"\"\"\n        baseline_report_files = {\"okay.py\": \"okay.py\"}\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found nothing\n        self.assertEqual(0, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were no results (no candidates found)\n        self.assertEqual(0, return_code)\n        self.assertIn(\"Total lines of code: 1\", return_value)\n        self.assertIn(new_candidates_no_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(baseline_no_issues_found, return_value)\n\n    def test_no_existing_with_new_candidates(self):\n        \"\"\"Tests when there are new candidates and no existing candidates\n\n        Test that bandit returns issues found in file that had no existing\n        candidates from baseline but now contain candidates.\n        \"\"\"\n        baseline_report_files = {\n            \"new_candidates-all.py\": \"new_candidates-none.py\"\n        }\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found nothing\n        self.assertEqual(0, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were results (candidates found)\n        self.assertEqual(1, return_code)\n        self.assertIn(new_candidates_all_total_lines, return_value)\n        self.assertIn(new_candidates_skip_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(xml_sax_issue_id, return_value)\n        self.assertIn(yaml_load_issue_id, return_value)\n        self.assertIn(shell_issue_id, return_value)\n        # candidate #1\n        self.assertIn(candidate_example_one, return_value)\n        # candidate #3\n        self.assertIn(candidate_example_three, return_value)\n        # candidate #5\n        self.assertIn(candidate_example_five, return_value)\n\n    def test_existing_and_new_candidates(self):\n        \"\"\"Tests when tere are new candidates and existing candidates\n\n        Test that bandit returns issues found in file with existing\n        candidates. The new candidates should be returned in this case.\n        \"\"\"\n        baseline_report_files = {\n            \"new_candidates-all.py\": \"new_candidates-some.py\"\n        }\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found results\n        self.assertEqual(1, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were results (candidates found)\n        self.assertEqual(1, return_code)\n        self.assertIn(new_candidates_all_total_lines, return_value)\n        self.assertIn(new_candidates_skip_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(xml_sax_issue_id, return_value)\n        self.assertIn(yaml_load_issue_id, return_value)\n        # candidate #3\n        self.assertIn(candidate_example_three, return_value)\n        # candidate #5\n        self.assertIn(candidate_example_five, return_value)\n\n    def test_no_new_candidates_include_nosec(self):\n        \"\"\"Test to check nosec references with no new candidates\n\n        Test that nosec references are included during a baseline test, which\n        would normally be ignored. In this test case, there are no new\n        candidates even while including the nosec references.\n        \"\"\"\n        self.baseline_commands.append(\"--ignore-nosec\")\n        baseline_report_files = {\n            \"new_candidates-all.py\": \"new_candidates-all.py\"\n        }\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found results\n        self.assertEqual(1, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were no results (candidates found)\n        self.assertEqual(0, return_code)\n        self.assertIn(new_candidates_all_total_lines, return_value)\n        self.assertIn(new_candidates_no_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(baseline_no_issues_found, return_value)\n\n    def test_new_candidates_include_nosec_only_nosecs(self):\n        \"\"\"Test to check nosec references with new only nosec candidates\n\n        Test that nosec references are included during a baseline test, which\n        would normally be ignored. In this test case, there are new candidates\n        which are specifically nosec references.\n        \"\"\"\n        self.baseline_commands.append(\"--ignore-nosec\")\n        baseline_report_files = {\n            \"new_candidates-nosec.py\": \"new_candidates-none.py\"\n        }\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found nothing\n        self.assertEqual(0, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were results (candidates found)\n        self.assertEqual(1, return_code)\n        self.assertIn(new_candidates_some_total_lines, return_value)\n        self.assertIn(new_candidates_no_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(xml_sax_issue_id, return_value)\n        self.assertIn(yaml_load_issue_id, return_value)\n        self.assertIn(shell_issue_id, return_value)\n        # candidate #2\n        self.assertIn(candidate_example_two, return_value)\n        # candidate #4\n        self.assertIn(candidate_example_four, return_value)\n        # candidate #6\n        self.assertIn(candidate_example_six, return_value)\n\n    def test_new_candidates_include_nosec_new_nosecs(self):\n        \"\"\"Test to check nosec references with new candidates, including nosecs\n\n        Test that nosec references are included during a baseline test, which\n        would normally be ignored. In this test case, there are new candidates\n        that also includes new nosec references as well.\n        \"\"\"\n        self.baseline_commands.append(\"--ignore-nosec\")\n        baseline_report_files = {\n            \"new_candidates-all.py\": \"new_candidates-none.py\"\n        }\n        target_directory, baseline_code = self._create_baseline(\n            baseline_report_files\n        )\n        # assert the initial baseline found nothing\n        self.assertEqual(0, baseline_code)\n        baseline_report = os.path.join(\n            target_directory, self.baseline_report_file\n        )\n        return_value, return_code = self._run_bandit_baseline(\n            target_directory, baseline_report\n        )\n        # assert there were results (candidates found)\n        self.assertEqual(1, return_code)\n        self.assertIn(new_candidates_all_total_lines, return_value)\n        self.assertIn(new_candidates_no_nosec_lines, return_value)\n        self.assertIn(baseline_no_skipped_files, return_value)\n        self.assertIn(xml_sax_issue_id, return_value)\n        self.assertIn(yaml_load_issue_id, return_value)\n        self.assertIn(shell_issue_id, return_value)\n        # candidate #1\n        self.assertIn(candidate_example_one, return_value)\n        # candidate #2\n        self.assertIn(candidate_example_two, return_value)\n        # candidate #3\n        self.assertIn(candidate_example_three, return_value)\n        # candidate #4\n        self.assertIn(candidate_example_four, return_value)\n        # candidate #5\n        self.assertIn(candidate_example_five, return_value)\n        # candidate #6\n        self.assertIn(candidate_example_six, return_value)\n"
  },
  {
    "path": "tests/functional/test_functional.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\nfrom contextlib import contextmanager\n\nimport testtools\n\nfrom bandit.core import config as b_config\nfrom bandit.core import constants as C\nfrom bandit.core import manager as b_manager\nfrom bandit.core import metrics\nfrom bandit.core import test_set as b_test_set\n\n\nclass FunctionalTests(testtools.TestCase):\n    \"\"\"Functional tests for bandit test plugins.\n\n    This set of tests runs bandit against each example file in turn\n    and records the score returned. This is compared to a known good value.\n    When new tests are added to an example the expected result should be\n    adjusted to match.\n    \"\"\"\n\n    def setUp(self):\n        super().setUp()\n        # NOTE(tkelsey): bandit is very sensitive to paths, so stitch\n        # them up here for the testing environment.\n        #\n        path = os.path.join(os.getcwd(), \"bandit\", \"plugins\")\n        b_conf = b_config.BanditConfig()\n        self.b_mgr = b_manager.BanditManager(b_conf, \"file\")\n        self.b_mgr.b_conf._settings[\"plugins_dir\"] = path\n        self.b_mgr.b_ts = b_test_set.BanditTestSet(config=b_conf)\n\n    @contextmanager\n    def with_test_set(self, ts):\n        \"\"\"A helper context manager to change the test set without\n        side-effects for any follow-up tests.\n        \"\"\"\n        orig_ts = self.b_mgr.b_ts\n        self.b_mgr.b_ts = ts\n        try:\n            yield\n        finally:\n            self.b_mgr.b_ts = orig_ts\n\n    def run_example(self, example_script, ignore_nosec=False):\n        \"\"\"A helper method to run the specified test\n\n        This method runs the test, which populates the self.b_mgr.scores\n        value. Call this directly if you need to run a test, but do not\n        need to test the resulting scores against specified values.\n        :param example_script: Filename of an example script to test\n        \"\"\"\n        path = os.path.join(os.getcwd(), \"examples\", example_script)\n        self.b_mgr.ignore_nosec = ignore_nosec\n        self.b_mgr.discover_files([path], True)\n        self.b_mgr.run_tests()\n\n    def check_example(self, example_script, expect, ignore_nosec=False):\n        \"\"\"A helper method to test the scores for example scripts.\n\n        :param example_script: Filename of an example script to test\n        :param expect: dict with expected counts of issue types\n        \"\"\"\n        # reset scores for subsequent calls to check_example\n        self.b_mgr.scores = []\n        self.run_example(example_script, ignore_nosec=ignore_nosec)\n\n        result = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n\n        for test_scores in self.b_mgr.scores:\n            for score_type in test_scores:\n                self.assertIn(score_type, expect)\n                for idx, rank in enumerate(C.RANKING):\n                    result[score_type][rank] = (\n                        test_scores[score_type][idx] // C.RANKING_VALUES[rank]\n                    )\n\n        self.assertDictEqual(expect, result)\n\n    def check_metrics(self, example_script, expect):\n        \"\"\"A helper method to test the metrics being returned.\n\n        :param example_script: Filename of an example script to test\n        :param expect: dict with expected values of metrics\n        \"\"\"\n        self.b_mgr.metrics = metrics.Metrics()\n        self.b_mgr.scores = []\n        self.run_example(example_script)\n\n        # test general metrics (excludes issue counts)\n        m = self.b_mgr.metrics.data\n        for k in expect:\n            if k != \"issues\":\n                self.assertEqual(expect[k], m[\"_totals\"][k])\n        # test issue counts\n        if \"issues\" in expect:\n            for criteria, default in C.CRITERIA:\n                for rank in C.RANKING:\n                    label = f\"{criteria}.{rank}\"\n                    expected = 0\n                    if expect[\"issues\"].get(criteria).get(rank):\n                        expected = expect[\"issues\"][criteria][rank]\n                    self.assertEqual(expected, m[\"_totals\"][label])\n\n    def test_binding(self):\n        \"\"\"Test the bind-to-0.0.0.0 example.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n        }\n        self.check_example(\"binding.py\", expect)\n\n    def test_crypto_md5(self):\n        \"\"\"Test the `hashlib.md5` example.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 16, \"HIGH\": 9},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 25},\n        }\n        self.check_example(\"crypto-md5.py\", expect)\n\n    def test_ciphers(self):\n        \"\"\"Test the `Crypto.Cipher` example.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 24},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 25},\n        }\n        self.check_example(\"ciphers.py\", expect)\n\n    def test_cipher_modes(self):\n        \"\"\"Test for insecure cipher modes.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"cipher-modes.py\", expect)\n\n    def test_eval(self):\n        \"\"\"Test the `eval` example.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"eval.py\", expect)\n\n    def test_mark_safe(self):\n        \"\"\"Test the `mark_safe` example.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"mark_safe.py\", expect)\n\n    def test_exec(self):\n        \"\"\"Test the `exec` example.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"exec.py\", expect)\n\n    def test_hardcoded_passwords(self):\n        \"\"\"Test for hard-coded passwords.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 16, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 16, \"HIGH\": 0},\n        }\n        self.check_example(\"hardcoded-passwords.py\", expect)\n\n    def test_hardcoded_tmp(self):\n        \"\"\"Test for hard-coded /tmp, /var/tmp, /dev/shm.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n        }\n        self.check_example(\"hardcoded-tmp.py\", expect)\n\n    def test_imports_aliases(self):\n        \"\"\"Test the `import X as Y` syntax.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 4, \"MEDIUM\": 1, \"HIGH\": 4},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 9},\n        }\n        self.check_example(\"imports-aliases.py\", expect)\n\n    def test_imports_from(self):\n        \"\"\"Test the `from X import Y` syntax.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 3, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"imports-from.py\", expect)\n\n    def test_imports_function(self):\n        \"\"\"Test the `__import__` function.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"imports-function.py\", expect)\n\n    def test_telnet_usage(self):\n        \"\"\"Test for `import telnetlib` and Telnet.* calls.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"telnetlib.py\", expect)\n\n    def test_ftp_usage(self):\n        \"\"\"Test for `import ftplib` and FTP.* calls.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"ftplib.py\", expect)\n\n    def test_imports(self):\n        \"\"\"Test for dangerous imports.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"imports.py\", expect)\n\n    def test_imports_using_importlib(self):\n        \"\"\"Test for dangerous imports using importlib.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 4, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"imports-with-importlib.py\", expect)\n\n    def test_mktemp(self):\n        \"\"\"Test for `tempfile.mktemp`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 4, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"mktemp.py\", expect)\n\n    def test_nonsense(self):\n        \"\"\"Test that a syntactically invalid module is skipped.\"\"\"\n        self.run_example(\"nonsense.py\")\n        self.assertEqual(1, len(self.b_mgr.skipped))\n\n    def test_okay(self):\n        \"\"\"Test a vulnerability-free file.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        self.check_example(\"okay.py\", expect)\n\n    def test_subdirectory_okay(self):\n        \"\"\"Test a vulnerability-free file under a subdirectory.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        self.check_example(\"init-py-test/subdirectory-okay.py\", expect)\n\n    def test_os_chmod(self):\n        \"\"\"Test setting file permissions.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 4, \"HIGH\": 8},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 11},\n        }\n        self.check_example(\"os-chmod.py\", expect)\n\n    def test_os_exec(self):\n        \"\"\"Test for `os.exec*`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 8, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 8, \"HIGH\": 0},\n        }\n        self.check_example(\"os-exec.py\", expect)\n\n    def test_os_popen(self):\n        \"\"\"Test for `os.popen`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 8, \"MEDIUM\": 0, \"HIGH\": 1},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 9},\n        }\n        self.check_example(\"os-popen.py\", expect)\n\n    def test_os_spawn(self):\n        \"\"\"Test for `os.spawn*`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 8, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 8, \"HIGH\": 0},\n        }\n        self.check_example(\"os-spawn.py\", expect)\n\n    def test_os_startfile(self):\n        \"\"\"Test for `os.startfile`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 3, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n        }\n        self.check_example(\"os-startfile.py\", expect)\n\n    def test_os_system(self):\n        \"\"\"Test for `os.system`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"os_system.py\", expect)\n\n    def test_pickle(self):\n        \"\"\"Test for the `pickle` module.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"pickle_deserialize.py\", expect)\n\n    def test_dill(self):\n        \"\"\"Test for the `dill` module.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"dill.py\", expect)\n\n    def test_shelve(self):\n        \"\"\"Test for the `shelve` module.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 2, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"shelve_open.py\", expect)\n\n    def test_jsonpickle(self):\n        \"\"\"Test for the `jsonpickle` module.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"jsonpickle.py\", expect)\n\n    def test_pandas_read_pickle(self):\n        \"\"\"Test for the `pandas.read_pickle` module.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"pandas_read_pickle.py\", expect)\n\n    def test_popen_wrappers(self):\n        \"\"\"Test the `popen2` and `commands` modules.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 7, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 7},\n        }\n        self.check_example(\"popen_wrappers.py\", expect)\n\n    def test_random_module(self):\n        \"\"\"Test for the `random` module.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 12, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 12},\n        }\n        self.check_example(\"random_module.py\", expect)\n\n    def test_requests_ssl_verify_disabled(self):\n        \"\"\"Test for the `requests` library skipping verification.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 18},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 18},\n        }\n        self.check_example(\"requests-ssl-verify-disabled.py\", expect)\n\n    def test_requests_without_timeout(self):\n        \"\"\"Test for the `requests` library missing timeouts.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 25, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 25, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        self.check_example(\"requests-missing-timeout.py\", expect)\n\n    def test_skip(self):\n        \"\"\"Test `#nosec` and `#noqa` comments.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 5, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 5},\n        }\n        self.check_example(\"skip.py\", expect)\n\n    def test_ignore_skip(self):\n        \"\"\"Test --ignore-nosec flag.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 7, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 7},\n        }\n        self.check_example(\"skip.py\", expect, ignore_nosec=True)\n\n    def test_sql_statements(self):\n        \"\"\"Test for SQL injection through string building.\"\"\"\n        expect = {\n            \"SEVERITY\": {\n                \"UNDEFINED\": 0,\n                \"LOW\": 0,\n                \"MEDIUM\": 23,\n                \"HIGH\": 0,\n            },\n            \"CONFIDENCE\": {\n                \"UNDEFINED\": 0,\n                \"LOW\": 11,\n                \"MEDIUM\": 12,\n                \"HIGH\": 0,\n            },\n        }\n        self.check_example(\"sql_statements.py\", expect)\n\n    def test_multiline_sql_statements(self):\n        \"\"\"\n        Test for SQL injection through string building using\n        multi-line strings.\n        \"\"\"\n        example_file = \"sql_multiline_statements.py\"\n        confidence_low_tests = 13\n        severity_medium_tests = 26\n        nosec_tests = 7\n        skipped_tests = 8\n        expect = {\n            \"SEVERITY\": {\n                \"UNDEFINED\": 0,\n                \"LOW\": 0,\n                \"MEDIUM\": severity_medium_tests,\n                \"HIGH\": 0,\n            },\n            \"CONFIDENCE\": {\n                \"UNDEFINED\": 0,\n                \"LOW\": confidence_low_tests,\n                \"MEDIUM\": 13,\n                \"HIGH\": 0,\n            },\n        }\n        expect_stats = {\n            \"nosec\": nosec_tests,\n            \"skipped_tests\": skipped_tests,\n        }\n        self.check_example(example_file, expect)\n        self.check_metrics(example_file, expect_stats)\n\n    def test_ssl_insecure_version(self):\n        \"\"\"Test for insecure SSL protocol versions.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 13, \"HIGH\": 9},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 14, \"HIGH\": 9},\n        }\n        self.check_example(\"ssl-insecure-version.py\", expect)\n\n    def test_subprocess_shell(self):\n        \"\"\"Test for `subprocess.Popen` with `shell=True`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 24, \"MEDIUM\": 1, \"HIGH\": 11},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 0, \"HIGH\": 35},\n        }\n        self.check_example(\"subprocess_shell.py\", expect)\n\n    def test_urlopen(self):\n        \"\"\"Test for dangerous URL opening.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 8, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 8},\n        }\n        self.check_example(\"urlopen.py\", expect)\n\n    def test_wildcard_injection(self):\n        \"\"\"Test for wildcard injection in shell commands.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 10, \"MEDIUM\": 0, \"HIGH\": 4},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 5, \"HIGH\": 9},\n        }\n        self.check_example(\"wildcard-injection.py\", expect)\n\n    def test_django_sql_injection(self):\n        \"\"\"Test insecure extra functions on Django.\"\"\"\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 11, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 11, \"HIGH\": 0},\n        }\n        self.check_example(\"django_sql_injection_extra.py\", expect)\n\n    def test_django_sql_injection_raw(self):\n        \"\"\"Test insecure raw functions on Django.\"\"\"\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 6, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 6, \"HIGH\": 0},\n        }\n        self.check_example(\"django_sql_injection_raw.py\", expect)\n\n    def test_yaml(self):\n        \"\"\"Test for `yaml.load`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 2, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"yaml_load.py\", expect)\n\n    def test_host_key_verification(self):\n        \"\"\"Test for ignoring host key verification.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 8},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 8, \"HIGH\": 0},\n        }\n        self.check_example(\"no_host_key_verification.py\", expect)\n\n    def test_jinja2_templating(self):\n        \"\"\"Test jinja templating for potential XSS bugs.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 5},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 2, \"HIGH\": 3},\n        }\n        self.check_example(\"jinja2_templating.py\", expect)\n\n    def test_mako_templating(self):\n        \"\"\"Test Mako templates for XSS.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"mako_templating.py\", expect)\n\n    def test_django_xss_secure(self):\n        \"\"\"Test false positives for Django XSS\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        with self.with_test_set(\n            b_test_set.BanditTestSet(\n                config=self.b_mgr.b_conf, profile={\"exclude\": [\"B308\"]}\n            )\n        ):\n            self.check_example(\"mark_safe_secure.py\", expect)\n\n    def test_django_xss_insecure(self):\n        \"\"\"Test for Django XSS via django.utils.safestring\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 29, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 29},\n        }\n        with self.with_test_set(\n            b_test_set.BanditTestSet(\n                config=self.b_mgr.b_conf, profile={\"exclude\": [\"B308\"]}\n            )\n        ):\n            self.check_example(\"mark_safe_insecure.py\", expect)\n\n    def test_xml(self):\n        \"\"\"Test xml vulnerabilities.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 4, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 5},\n        }\n        self.check_example(\"xml_etree_celementtree.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 2, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"xml_expatbuilder.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 2, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"xml_pulldom.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"xml_xmlrpc.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 4, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 5},\n        }\n        self.check_example(\"xml_etree_elementtree.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"xml_expatreader.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 2, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"xml_minidom.py\", expect)\n\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 6, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 8},\n        }\n        self.check_example(\"xml_sax.py\", expect)\n\n    def test_httpoxy(self):\n        \"\"\"Test httpoxy vulnerability.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"httpoxy_cgihandler.py\", expect)\n        self.check_example(\"httpoxy_twisted_script.py\", expect)\n        self.check_example(\"httpoxy_twisted_directory.py\", expect)\n\n    def test_asserts(self):\n        \"\"\"Test catching the use of assert.\"\"\"\n        test = next(\n            x\n            for x in self.b_mgr.b_ts.tests[\"Assert\"]\n            if x.__name__ == \"assert_used\"\n        )\n\n        test._config = {\"skips\": []}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"assert.py\", expect)\n\n        test._config = {\"skips\": [\"*assert.py\"]}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        self.check_example(\"assert.py\", expect)\n\n        test._config = {}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"assert.py\", expect)\n\n    def test_paramiko_injection(self):\n        \"\"\"Test paramiko command execution.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n        }\n        self.check_example(\"paramiko_injection.py\", expect)\n\n    def test_partial_path(self):\n        \"\"\"Test process spawning with partial file paths.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 11, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 11},\n        }\n        self.check_example(\"partial_path_process.py\", expect)\n\n    def test_try_except_continue(self):\n        \"\"\"Test try, except, continue detection.\"\"\"\n        test = next(\n            x\n            for x in self.b_mgr.b_ts.tests[\"ExceptHandler\"]\n            if x.__name__ == \"try_except_continue\"\n        )\n\n        test._config = {\"check_typed_exception\": True}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 3, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"try_except_continue.py\", expect)\n\n        test._config = {\"check_typed_exception\": False}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"try_except_continue.py\", expect)\n\n    def test_try_except_pass(self):\n        \"\"\"Test try, except pass detection.\"\"\"\n        test = next(\n            x\n            for x in self.b_mgr.b_ts.tests[\"ExceptHandler\"]\n            if x.__name__ == \"try_except_pass\"\n        )\n\n        test._config = {\"check_typed_exception\": True}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 3, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"try_except_pass.py\", expect)\n\n        test._config = {\"check_typed_exception\": False}\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 2, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"try_except_pass.py\", expect)\n\n    def test_metric_gathering(self):\n        expect = {\n            \"nosec\": 2,\n            \"loc\": 7,\n            \"issues\": {\"CONFIDENCE\": {\"HIGH\": 5}, \"SEVERITY\": {\"LOW\": 5}},\n        }\n        self.check_metrics(\"skip.py\", expect)\n        expect = {\n            \"nosec\": 0,\n            \"loc\": 4,\n            \"issues\": {\"CONFIDENCE\": {\"HIGH\": 2}, \"SEVERITY\": {\"LOW\": 2}},\n        }\n        self.check_metrics(\"imports.py\", expect)\n\n    def test_weak_cryptographic_key(self):\n        \"\"\"Test for weak key sizes.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 8, \"HIGH\": 8},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 16},\n        }\n        self.check_example(\"weak_cryptographic_key_sizes.py\", expect)\n\n    def test_multiline_code(self):\n        \"\"\"Test issues in multiline statements return code as expected.\"\"\"\n        self.run_example(\"multiline_statement.py\")\n        self.assertEqual(0, len(self.b_mgr.skipped))\n        self.assertEqual(1, len(self.b_mgr.files_list))\n        self.assertTrue(\n            self.b_mgr.files_list[0].endswith(\"multiline_statement.py\")\n        )\n\n        issues = self.b_mgr.get_issue_list()\n        self.assertEqual(3, len(issues))\n        self.assertTrue(\n            issues[0].fname.endswith(\"examples/multiline_statement.py\")\n        )\n        self.assertEqual(1, issues[0].lineno)\n        self.assertEqual(list(range(1, 2)), issues[0].linerange)\n        self.assertIn(\"subprocess\", issues[0].get_code())\n        self.assertEqual(5, issues[1].lineno)\n        self.assertEqual(list(range(3, 6 + 1)), issues[1].linerange)\n        self.assertIn(\"shell=True\", issues[1].get_code())\n        self.assertEqual(11, issues[2].lineno)\n        self.assertEqual(list(range(8, 13 + 1)), issues[2].linerange)\n        self.assertIn(\"shell=True\", issues[2].get_code())\n\n    def test_code_line_numbers(self):\n        self.run_example(\"binding.py\")\n        issues = self.b_mgr.get_issue_list()\n\n        code_lines = issues[0].get_code().splitlines()\n        lineno = issues[0].lineno\n        self.assertEqual(\"%i \" % (lineno - 1), code_lines[0][:2])\n        self.assertEqual(\"%i \" % (lineno), code_lines[1][:2])\n        self.assertEqual(\"%i \" % (lineno + 1), code_lines[2][:2])\n\n    def test_flask_debug_true(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n        }\n        self.check_example(\"flask_debug.py\", expect)\n\n    def test_nosec(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 5, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 5},\n        }\n        self.check_example(\"nosec.py\", expect)\n\n    def test_baseline_filter(self):\n        issue_text = (\n            \"A Flask app appears to be run with debug=True, which \"\n            \"exposes the Werkzeug debugger and allows the execution \"\n            \"of arbitrary code.\"\n        )\n        json = f\"\"\"{{\n          \"results\": [\n            {{\n              \"code\": \"...\",\n              \"filename\": \"{os.getcwd()}/examples/flask_debug.py\",\n              \"issue_confidence\": \"MEDIUM\",\n              \"issue_severity\": \"HIGH\",\n              \"issue_cwe\": {{\n                \"id\": 94,\n                \"link\": \"https://cwe.mitre.org/data/definitions/94.html\"\n              }},\n              \"issue_text\": \"{issue_text}\",\n              \"line_number\": 10,\n              \"col_offset\": 0,\n              \"line_range\": [\n                10\n              ],\n              \"test_name\": \"flask_debug_true\",\n              \"test_id\": \"B201\"\n            }}\n          ]\n        }}\n        \"\"\"\n\n        self.b_mgr.populate_baseline(json)\n        self.run_example(\"flask_debug.py\")\n        self.assertEqual(1, len(self.b_mgr.baseline))\n        self.assertEqual({}, self.b_mgr.get_issue_list())\n\n    def test_unverified_context(self):\n        \"\"\"Test for `ssl._create_unverified_context`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        self.check_example(\"unverified_context.py\", expect)\n\n    def test_hashlib_new_insecure_functions(self):\n        \"\"\"Test insecure hash functions created by `hashlib.new`.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 9},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 9},\n        }\n        self.check_example(\"hashlib_new_insecure_functions.py\", expect)\n\n    def test_blacklist_pycrypto(self):\n        \"\"\"Test importing pycrypto module\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        self.check_example(\"pycrypto.py\", expect)\n\n    def test_no_blacklist_pycryptodome(self):\n        \"\"\"Test importing pycryptodome module\n\n        make sure it's no longer blacklisted\n        \"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        self.check_example(\"pycryptodome.py\", expect)\n\n    def test_blacklist_pyghmi(self):\n        \"\"\"Test calling pyghmi methods\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 0, \"HIGH\": 1},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 1},\n        }\n        self.check_example(\"pyghmi.py\", expect)\n\n    def test_snmp_security_check(self):\n        \"\"\"Test insecure and weak crypto usage of SNMP.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"snmp.py\", expect)\n\n    def test_tarfile_unsafe_members(self):\n        \"\"\"Test insecure usage of tarfile.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 2, \"HIGH\": 2},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 1, \"MEDIUM\": 2, \"HIGH\": 2},\n        }\n        self.check_example(\"tarfile_extractall.py\", expect)\n\n    def test_pytorch_load(self):\n        \"\"\"Test insecure usage of torch.load.\"\"\"\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 3, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 3},\n        }\n        self.check_example(\"pytorch_load.py\", expect)\n\n    def test_trojansource(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n        }\n        self.check_example(\"trojansource.py\", expect)\n\n    def test_trojansource_latin1(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 0},\n        }\n        self.check_example(\"trojansource_latin1.py\", expect)\n\n    def test_markupsafe_markup_xss(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 4, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 4},\n        }\n        self.check_example(\"markupsafe_markup_xss.py\", expect)\n\n    def test_markupsafe_markup_xss_extend_markup_names(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 2, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 2},\n        }\n        b_conf = b_config.BanditConfig()\n        b_conf.config[\"markupsafe_xss\"] = {\n            \"extend_markup_names\": [\"webhelpers.html.literal\"]\n        }\n        with self.with_test_set(b_test_set.BanditTestSet(config=b_conf)):\n            self.check_example(\n                \"markupsafe_markup_xss_extend_markup_names.py\", expect\n            )\n\n    def test_markupsafe_markup_xss_allowed_calls(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 1, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 1},\n        }\n        b_conf = b_config.BanditConfig()\n        b_conf.config[\"markupsafe_xss\"] = {\"allowed_calls\": [\"bleach.clean\"]}\n        with self.with_test_set(b_test_set.BanditTestSet(config=b_conf)):\n            self.check_example(\n                \"markupsafe_markup_xss_allowed_calls.py\", expect\n            )\n\n    def test_huggingface_unsafe_download(self):\n        expect = {\n            \"SEVERITY\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 15, \"HIGH\": 0},\n            \"CONFIDENCE\": {\"UNDEFINED\": 0, \"LOW\": 0, \"MEDIUM\": 0, \"HIGH\": 15},\n        }\n        self.check_example(\"huggingface_unsafe_download.py\", expect)\n"
  },
  {
    "path": "tests/functional/test_runtime.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\nimport subprocess\n\nimport testtools\n\n\nclass RuntimeTests(testtools.TestCase):\n    def _test_runtime(self, cmdlist, infile=None):\n        process = subprocess.Popen(\n            cmdlist,\n            stdin=infile if infile else subprocess.PIPE,\n            stdout=subprocess.PIPE,\n            stderr=subprocess.STDOUT,\n            close_fds=True,\n        )\n        stdout, stderr = process.communicate()\n        retcode = process.poll()\n        return (retcode, stdout.decode(\"utf-8\"))\n\n    def _test_example(self, cmdlist, targets):\n        for t in targets:\n            cmdlist.append(os.path.join(os.getcwd(), \"examples\", t))\n        return self._test_runtime(cmdlist)\n\n    def test_no_arguments(self):\n        (retcode, output) = self._test_runtime(\n            [\n                \"bandit\",\n            ]\n        )\n        self.assertEqual(2, retcode)\n        self.assertIn(\"usage: bandit [-h]\", output)\n\n    def test_piped_input(self):\n        with open(\"examples/imports.py\") as infile:\n            (retcode, output) = self._test_runtime([\"bandit\", \"-\"], infile)\n            self.assertEqual(1, retcode)\n            self.assertIn(\"Total lines of code: 4\", output)\n            self.assertIn(\"Low: 2\", output)\n            self.assertIn(\"High: 2\", output)\n            self.assertIn(\"Files skipped (0):\", output)\n            self.assertIn(\"Issue: [B403:blacklist] Consider possible\", output)\n            self.assertIn(\"<stdin>:2\", output)\n            self.assertIn(\"<stdin>:4\", output)\n\n    def test_nonexistent_config(self):\n        (retcode, output) = self._test_runtime(\n            [\"bandit\", \"-c\", \"nonexistent.yml\", \"xx.py\"]\n        )\n        self.assertEqual(2, retcode)\n        self.assertIn(\"nonexistent.yml : Could not read config file.\", output)\n\n    def test_help_arg(self):\n        (retcode, output) = self._test_runtime([\"bandit\", \"-h\"])\n        self.assertEqual(0, retcode)\n        self.assertIn(\n            \"Bandit - a Python source code security analyzer\", output\n        )\n        self.assertIn(\"usage: bandit [-h]\", output)\n        self.assertIn(\"positional arguments:\", output)\n        self.assertIn(\"tests were discovered and loaded:\", output)\n\n    # test examples (use _test_example() to wrap in config location argument\n    def test_example_nonexistent(self):\n        (retcode, output) = self._test_example(\n            [\n                \"bandit\",\n            ],\n            [\n                \"nonexistent.py\",\n            ],\n        )\n        self.assertEqual(0, retcode)\n        self.assertIn(\"Files skipped (1):\", output)\n        self.assertIn(\"nonexistent.py (No such file or directory\", output)\n\n    def test_example_okay(self):\n        (retcode, output) = self._test_example(\n            [\n                \"bandit\",\n            ],\n            [\n                \"okay.py\",\n            ],\n        )\n        self.assertEqual(0, retcode)\n        self.assertIn(\"Total lines of code: 1\", output)\n        self.assertIn(\"Files skipped (0):\", output)\n        self.assertIn(\"No issues identified.\", output)\n\n    def test_example_nonsense(self):\n        (retcode, output) = self._test_example(\n            [\n                \"bandit\",\n            ],\n            [\n                \"nonsense.py\",\n            ],\n        )\n        self.assertEqual(0, retcode)\n        self.assertIn(\"Files skipped (1):\", output)\n        self.assertIn(\"nonsense.py (syntax error while parsing AST\", output)\n\n    def test_example_nonsense2(self):\n        (retcode, output) = self._test_example(\n            [\n                \"bandit\",\n            ],\n            [\n                \"nonsense2.py\",\n            ],\n        )\n        self.assertEqual(0, retcode)\n        self.assertIn(\"Files skipped (1):\", output)\n        self.assertIn(\"nonsense2.py (syntax error while parsing AST\", output)\n\n    def test_example_imports(self):\n        (retcode, output) = self._test_example(\n            [\n                \"bandit\",\n            ],\n            [\n                \"imports.py\",\n            ],\n        )\n        self.assertEqual(1, retcode)\n        self.assertIn(\"Total lines of code: 4\", output)\n        self.assertIn(\"Low: 2\", output)\n        self.assertIn(\"High: 2\", output)\n        self.assertIn(\"Files skipped (0):\", output)\n        self.assertIn(\"Issue: [B403:blacklist] Consider possible\", output)\n        self.assertIn(\"imports.py:2\", output)\n        self.assertIn(\"imports.py:4\", output)\n"
  },
  {
    "path": "tests/unit/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/cli/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/cli/test_baseline.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\nimport subprocess\nfrom unittest import mock\n\nimport fixtures\nimport git\nimport testtools\n\nfrom bandit.cli import baseline\n\nconfig = \"\"\"\ninclude:\n    - '*.py'\n    - '*.pyw'\n\nprofiles:\n    test:\n        include:\n            - start_process_with_a_shell\n\nshell_injection:\n    subprocess: []\n    no_shell: []\n    shell:\n        - os.system\n\"\"\"\n\n\nclass BanditBaselineToolTests(testtools.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        # Set up prior to running test class\n        # read in content used for temporary file contents\n        with open(\"examples/mktemp.py\") as fd:\n            cls.temp_file_contents = fd.read()\n\n    def setUp(self):\n        # Set up prior to run each test case\n        super().setUp()\n        self.current_directory = os.getcwd()\n\n    def tearDown(self):\n        # Tear down after running each test case\n        super().tearDown()\n        os.chdir(self.current_directory)\n\n    def test_bandit_baseline(self):\n        # Tests running bandit via the CLI (baseline) with benign and malicious\n        # content\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n\n        # get benign and findings examples\n        with open(\"examples/okay.py\") as fd:\n            benign_contents = fd.read()\n\n        with open(\"examples/os_system.py\") as fd:\n            malicious_contents = fd.read()\n\n        contents = {\n            \"benign_one.py\": benign_contents,\n            \"benign_two.py\": benign_contents,\n            \"malicious.py\": malicious_contents,\n        }\n\n        # init git repo, change directory to it\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial commit\")\n        os.chdir(repo_directory)\n\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(config)\n\n        # create three branches, first has only benign, second adds malicious,\n        # third adds benign\n\n        branches = [\n            {\n                \"name\": \"benign1\",\n                \"files\": [\"benign_one.py\"],\n                \"expected_return\": 0,\n            },\n            {\n                \"name\": \"malicious\",\n                \"files\": [\"benign_one.py\", \"malicious.py\"],\n                \"expected_return\": 1,\n            },\n            {\n                \"name\": \"benign2\",\n                \"files\": [\"benign_one.py\", \"malicious.py\", \"benign_two.py\"],\n                \"expected_return\": 0,\n            },\n        ]\n\n        baseline_command = [\n            \"bandit-baseline\",\n            \"-c\",\n            \"bandit.yaml\",\n            \"-r\",\n            \".\",\n            \"-p\",\n            \"test\",\n        ]\n\n        for branch in branches:\n            branch[\"branch\"] = git_repo.create_head(branch[\"name\"])\n            git_repo.head.reference = branch[\"branch\"]\n            git_repo.head.reset(working_tree=True)\n\n            for f in branch[\"files\"]:\n                with open(f, \"w\") as fd:\n                    fd.write(contents[f])\n\n            git_repo.index.add(branch[\"files\"])\n            git_repo.index.commit(branch[\"name\"])\n\n            self.assertEqual(\n                branch[\"expected_return\"], subprocess.call(baseline_command)\n            )\n\n    def test_main_non_repo(self):\n        # Test that bandit gracefully exits when there is no git repository\n        # when calling main\n        repo_dir = self.useFixture(fixtures.TempDir()).path\n        os.chdir(repo_dir)\n\n        # assert the system exits with code 2\n        self.assertRaisesRegex(SystemExit, \"2\", baseline.main)\n\n    def test_main_git_command_failure(self):\n        # Test that bandit does not run when the Git command fails\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        additional_content = \"additional_file.py\"\n        with open(additional_content, \"w\") as fd:\n            fd.write(self.temp_file_contents)\n        git_repo.index.add([additional_content])\n        git_repo.index.commit(\"Additional Content\")\n\n        with mock.patch(\"git.Repo.commit\") as mock_git_repo_commit:\n            mock_git_repo_commit.side_effect = git.exc.GitCommandError(\n                \"commit\", \"\"\n            )\n\n            # assert the system exits with code 2\n            self.assertRaisesRegex(SystemExit, \"2\", baseline.main)\n\n    def test_main_no_parent_commit(self):\n        # Test that bandit exits when there is no parent commit detected when\n        # calling main\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        # assert the system exits with code 2\n        self.assertRaisesRegex(SystemExit, \"2\", baseline.main)\n\n    def test_main_subprocess_error(self):\n        # Test that bandit handles a CalledProcessError when attempting to run\n        # bandit baseline via a subprocess\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        additional_content = \"additional_file.py\"\n        with open(additional_content, \"w\") as fd:\n            fd.write(self.temp_file_contents)\n        git_repo.index.add([additional_content])\n        git_repo.index.commit(\"Additional Content\")\n\n        with mock.patch(\"subprocess.check_output\") as mock_check_output:\n            mock_bandit_cmd = \"bandit_mock -b temp_file.txt\"\n            mock_check_output.side_effect = subprocess.CalledProcessError(\n                \"3\", mock_bandit_cmd\n            )\n\n            # assert the system exits with code 3 (returned from\n            # CalledProcessError)\n            self.assertRaisesRegex(SystemExit, \"3\", baseline.main)\n\n    def test_init_logger(self):\n        # Test whether the logger was initialized when calling init_logger\n        baseline.init_logger()\n        logger = baseline.LOG\n\n        # verify that logger was initialized\n        self.assertIsNotNone(logger)\n\n    def test_initialize_no_repo(self):\n        # Test that bandit does not run when there is no current git\n        # repository when calling initialize\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(repo_directory)\n\n        return_value = baseline.initialize()\n\n        # assert bandit did not run due to no git repo\n        self.assertEqual((None, None, None), return_value)\n\n    def test_initialize_git_command_failure(self):\n        # Test that bandit does not run when the Git command fails\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        additional_content = \"additional_file.py\"\n        with open(additional_content, \"w\") as fd:\n            fd.write(self.temp_file_contents)\n        git_repo.index.add([additional_content])\n        git_repo.index.commit(\"Additional Content\")\n\n        with mock.patch(\"git.Repo\") as mock_git_repo:\n            mock_git_repo.side_effect = git.exc.GitCommandNotFound(\"clone\", \"\")\n\n            return_value = baseline.initialize()\n\n            # assert bandit did not run due to git command failure\n            self.assertEqual((None, None, None), return_value)\n\n    def test_initialize_dirty_repo(self):\n        # Test that bandit does not run when the current git repository is\n        # 'dirty' when calling the initialize method\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        # make the git repo 'dirty'\n        with open(\"dirty_file.py\", \"w\") as fd:\n            fd.write(self.temp_file_contents)\n        git_repo.index.add([\"dirty_file.py\"])\n\n        return_value = baseline.initialize()\n\n        # assert bandit did not run due to dirty repo\n        self.assertEqual((None, None, None), return_value)\n\n    @mock.patch(\"sys.argv\", [\"bandit\", \"-f\", \"txt\", \"test\"])\n    def test_initialize_existing_report_file(self):\n        # Test that bandit does not run when the output file exists (and the\n        # provided output format does not match the default format) when\n        # calling the initialize method\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        # create an existing version of output report file\n        existing_report = f\"{baseline.report_basename}.txt\"\n        with open(existing_report, \"w\") as fd:\n            fd.write(self.temp_file_contents)\n\n        return_value = baseline.initialize()\n\n        # assert bandit did not run due to existing report file\n        self.assertEqual((None, None, None), return_value)\n\n    @mock.patch(\n        \"bandit.cli.baseline.bandit_args\", [\"-o\", \"bandit_baseline_result\"]\n    )\n    def test_initialize_with_output_argument(self):\n        # Test that bandit does not run when the '-o' (output) argument is\n        # specified\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        return_value = baseline.initialize()\n\n        # assert bandit did not run due to provided -o (--ouput) argument\n        self.assertEqual((None, None, None), return_value)\n\n    def test_initialize_existing_temp_file(self):\n        # Test that bandit does not run when the temporary output file exists\n        # when calling the initialize method\n        repo_directory = self.useFixture(fixtures.TempDir()).path\n        git_repo = git.Repo.init(repo_directory)\n        git_repo.index.commit(\"Initial Commit\")\n        os.chdir(repo_directory)\n\n        # create an existing version of temporary output file\n        existing_temp_file = baseline.baseline_tmp_file\n        with open(existing_temp_file, \"w\") as fd:\n            fd.write(self.temp_file_contents)\n\n        return_value = baseline.initialize()\n\n        # assert bandit did not run due to existing temporary report file\n        self.assertEqual((None, None, None), return_value)\n"
  },
  {
    "path": "tests/unit/cli/test_config_generator.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nimport importlib\nimport logging\nfrom unittest import mock\n\nimport testtools\nimport yaml\n\nfrom bandit.cli import config_generator\nfrom bandit.core import extension_loader\nfrom bandit.core import test_properties as test\n\n\ndef gen_config(name):\n    return {\"test\": \"test data\"}\n\n\n@test.takes_config(\"test\")\n@test.checks(\"Str\")\ndef _test_plugin(context, conf):\n    pass\n\n\nclass BanditConfigGeneratorLoggerTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        self.logger = logging.getLogger(config_generator.__name__)\n        self.original_logger_handlers = self.logger.handlers\n        self.original_logger_level = self.logger.level\n        self.logger.handlers = []\n\n    def tearDown(self):\n        super().tearDown()\n        self.logger.handlers = self.original_logger_handlers\n        self.logger.level = self.original_logger_level\n\n    def test_init_logger(self):\n        # Test that a logger was properly initialized\n        config_generator.init_logger()\n        self.assertIsNotNone(self.logger)\n        self.assertNotEqual([], self.logger.handlers)\n        self.assertEqual(logging.INFO, self.logger.level)\n\n\nclass BanditConfigGeneratorTests(testtools.TestCase):\n    @mock.patch(\"sys.argv\", [\"bandit-config-generator\"])\n    def test_parse_args_no_defaults(self):\n        # Without arguments, the generator should just show help and exit\n        self.assertRaises(SystemExit, config_generator.parse_args)\n\n    @mock.patch(\"sys.argv\", [\"bandit-config-generator\", \"--show-defaults\"])\n    def test_parse_args_show_defaults(self):\n        # Test that the config generator does show default plugin settings\n        return_value = config_generator.parse_args()\n        self.assertTrue(return_value.show_defaults)\n\n    @mock.patch(\"sys.argv\", [\"bandit-config-generator\", \"--out\", \"dummyfile\"])\n    def test_parse_args_out_file(self):\n        # Test config generator get proper output file when specified\n        return_value = config_generator.parse_args()\n        self.assertEqual(\"dummyfile\", return_value.output_file)\n\n    def test_get_config_settings(self):\n        config = {}\n        for plugin in extension_loader.MANAGER.plugins:\n            function = plugin.plugin\n            if hasattr(plugin.plugin, \"_takes_config\"):\n                module = importlib.import_module(function.__module__)\n                config[plugin.name] = module.gen_config(function._takes_config)\n        settings = config_generator.get_config_settings()\n        self.assertEqual(\n            yaml.safe_dump(config, default_flow_style=False), settings\n        )\n\n    @mock.patch(\"sys.argv\", [\"bandit-config-generator\", \"--show-defaults\"])\n    def test_main_show_defaults(self):\n        # Test that the config generator does show defaults and returns 0\n        with mock.patch(\n            \"bandit.cli.config_generator.get_config_settings\"\n        ) as mock_config_settings:\n            return_value = config_generator.main()\n            # The get_config_settings function should have been called\n            self.assertTrue(mock_config_settings.called)\n            self.assertEqual(0, return_value)\n"
  },
  {
    "path": "tests/unit/cli/test_main.py",
    "content": "#    Copyright 2016 IBM Corp.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport logging\nimport os\nfrom unittest import mock\n\nimport fixtures\nimport testtools\n\nfrom bandit.cli import main as bandit\nfrom bandit.core import extension_loader as ext_loader\nfrom bandit.core import utils\n\nbandit_config_content = \"\"\"\ninclude:\n    - '*.py'\n    - '*.pyw'\n\nprofiles:\n    test:\n        include:\n            - start_process_with_a_shell\n\nshell_injection:\n    subprocess:\n\n    shell:\n        - os.system\n\"\"\"\n\nbandit_baseline_content = \"\"\"{\n    \"results\": [\n        {\n            \"code\": \"some test code\",\n            \"filename\": \"test_example.py\",\n            \"issue_severity\": \"low\",\n            \"issue_confidence\": \"low\",\n            \"issue_text\": \"test_issue\",\n            \"test_name\": \"some_test\",\n            \"test_id\": \"x\",\n            \"line_number\": \"n\",\n            \"line_range\": \"n-m\"\n        }\n    ]\n}\n\"\"\"\n\n\nclass BanditCLIMainLoggerTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        self.logger = logging.getLogger()\n        self.original_logger_handlers = self.logger.handlers\n        self.original_logger_level = self.logger.level\n        self.logger.handlers = []\n\n    def tearDown(self):\n        super().tearDown()\n        self.logger.handlers = self.original_logger_handlers\n        self.logger.level = self.original_logger_level\n\n    def test_init_logger(self):\n        # Test that a logger was properly initialized\n        bandit._init_logger()\n\n        self.assertIsNotNone(self.logger)\n        self.assertNotEqual(self.logger.handlers, [])\n        self.assertEqual(logging.INFO, self.logger.level)\n\n    def test_init_logger_debug_mode(self):\n        # Test that the logger's level was set at 'DEBUG'\n        bandit._init_logger(logging.DEBUG)\n        self.assertEqual(logging.DEBUG, self.logger.level)\n\n\nclass BanditCLIMainTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        self.current_directory = os.getcwd()\n\n    def tearDown(self):\n        super().tearDown()\n        os.chdir(self.current_directory)\n\n    def test_get_options_from_ini_no_ini_path_no_target(self):\n        # Test that no config options are loaded when no ini path or target\n        # directory are provided\n        self.assertIsNone(bandit._get_options_from_ini(None, []))\n\n    def test_get_options_from_ini_empty_directory_no_target(self):\n        # Test that no config options are loaded when an empty directory is\n        # provided as the ini path and no target directory is provided\n        ini_directory = self.useFixture(fixtures.TempDir()).path\n        self.assertIsNone(bandit._get_options_from_ini(ini_directory, []))\n\n    def test_get_options_from_ini_no_ini_path_no_bandit_files(self):\n        # Test that no config options are loaded when no ini path is provided\n        # and the target directory contains no bandit config files (.bandit)\n        target_directory = self.useFixture(fixtures.TempDir()).path\n        self.assertIsNone(\n            bandit._get_options_from_ini(None, [target_directory])\n        )\n\n    def test_get_options_from_ini_no_ini_path_multi_bandit_files(self):\n        # Test that bandit exits when no ini path is provided and the target\n        # directory(s) contain multiple bandit config files (.bandit)\n        target_directory = self.useFixture(fixtures.TempDir()).path\n        second_config = \"second_config_directory\"\n        os.mkdir(os.path.join(target_directory, second_config))\n        bandit_config_one = os.path.join(target_directory, \".bandit\")\n        bandit_config_two = os.path.join(\n            target_directory, second_config, \".bandit\"\n        )\n        bandit_files = [bandit_config_one, bandit_config_two]\n        for bandit_file in bandit_files:\n            with open(bandit_file, \"w\") as fd:\n                fd.write(bandit_config_content)\n        self.assertRaisesRegex(\n            SystemExit,\n            \"2\",\n            bandit._get_options_from_ini,\n            None,\n            [target_directory],\n        )\n\n    def test_init_extensions(self):\n        # Test that an extension loader manager is returned\n        self.assertEqual(ext_loader.MANAGER, bandit._init_extensions())\n\n    def test_log_option_source_arg_val(self):\n        # Test that the command argument value is returned when provided\n        # with None or a string default value\n        arg_val = \"file\"\n        ini_val = \"vuln\"\n        option_name = \"aggregate\"\n        for default_val in (None, \"default\"):\n            self.assertEqual(\n                arg_val,\n                bandit._log_option_source(\n                    default_val, arg_val, ini_val, option_name\n                ),\n            )\n\n    def test_log_option_source_ini_value(self):\n        # Test that the ini value is returned when no command argument is\n        # provided\n        default_val = None\n        ini_val = \"vuln\"\n        option_name = \"aggregate\"\n        self.assertEqual(\n            ini_val,\n            bandit._log_option_source(default_val, None, ini_val, option_name),\n        )\n\n    def test_log_option_source_ini_val_with_str_default_and_no_arg_val(self):\n        # Test that the ini value is returned when no command argument is\n        # provided\n        default_val = \"file\"\n        arg_val = \"file\"\n        ini_val = \"vuln\"\n        option_name = \"aggregate\"\n        self.assertEqual(\n            ini_val,\n            bandit._log_option_source(\n                default_val, arg_val, ini_val, option_name\n            ),\n        )\n\n    def test_log_option_source_no_values(self):\n        # Test that None is returned when no command argument or ini value are\n        # provided\n        option_name = \"aggregate\"\n        self.assertIsNone(\n            bandit._log_option_source(None, None, None, option_name)\n        )\n\n    @mock.patch(\"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"test\"])\n    def test_main_config_unopenable(self):\n        # Test that bandit exits when a config file cannot be opened\n        with mock.patch(\"bandit.core.config.__init__\") as mock_bandit_config:\n            mock_bandit_config.side_effect = utils.ConfigError(\"\", \"\")\n            # assert a SystemExit with code 2\n            self.assertRaisesRegex(SystemExit, \"2\", bandit.main)\n\n    @mock.patch(\"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"test\"])\n    def test_main_invalid_config(self):\n        # Test that bandit exits when a config file contains invalid YAML\n        # content\n        with mock.patch(\n            \"bandit.core.config.BanditConfig.__init__\"\n        ) as mock_bandit_config:\n            mock_bandit_config.side_effect = utils.ConfigError(\"\", \"\")\n            # assert a SystemExit with code 2\n            self.assertRaisesRegex(SystemExit, \"2\", bandit.main)\n\n    @mock.patch(\"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"test\"])\n    def test_main_handle_ini_options(self):\n        # Test that bandit handles cmdline args from a bandit.yaml file\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        with mock.patch(\n            \"bandit.cli.main._get_options_from_ini\"\n        ) as mock_get_opts:\n            mock_get_opts.return_value = {\n                \"exclude\": \"/tmp\",\n                \"skips\": \"skip_test\",\n                \"tests\": \"some_test\",\n            }\n\n            with mock.patch(\"bandit.cli.main.LOG.error\") as err_mock:\n                # SystemExit with code 2 when test not found in profile\n                self.assertRaisesRegex(SystemExit, \"2\", bandit.main)\n                self.assertEqual(\n                    str(err_mock.call_args[0][0]),\n                    \"No tests would be run, please check the profile.\",\n                )\n\n    @mock.patch(\n        \"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"-p\", \"bad\", \"test\"]\n    )\n    def test_main_profile_not_found(self):\n        # Test that bandit exits when an invalid profile name is provided\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        # assert a SystemExit with code 2\n        with mock.patch(\"bandit.cli.main.LOG.error\") as err_mock:\n            self.assertRaisesRegex(SystemExit, \"2\", bandit.main)\n            self.assertEqual(\n                str(err_mock.call_args[0][0]),\n                \"Unable to find profile (bad) in config file: bandit.yaml\",\n            )\n\n    @mock.patch(\n        \"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"-b\", \"base.json\", \"test\"]\n    )\n    def test_main_baseline_ioerror(self):\n        # Test that bandit exits when encountering an IOError while reading\n        # baseline data\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        with open(\"base.json\", \"w\") as fd:\n            fd.write(bandit_baseline_content)\n        with mock.patch(\n            \"bandit.core.manager.BanditManager.populate_baseline\"\n        ) as mock_mgr_pop_bl:\n            mock_mgr_pop_bl.side_effect = IOError\n            # assert a SystemExit with code 2\n            self.assertRaisesRegex(SystemExit, \"2\", bandit.main)\n\n    @mock.patch(\n        \"sys.argv\",\n        [\n            \"bandit\",\n            \"-c\",\n            \"bandit.yaml\",\n            \"-b\",\n            \"base.json\",\n            \"-f\",\n            \"csv\",\n            \"test\",\n        ],\n    )\n    def test_main_invalid_output_format(self):\n        # Test that bandit exits when an invalid output format is selected\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        with open(\"base.json\", \"w\") as fd:\n            fd.write(bandit_baseline_content)\n        # assert a SystemExit with code 2\n        self.assertRaisesRegex(SystemExit, \"2\", bandit.main)\n\n    @mock.patch(\n        \"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"test\", \"-o\", \"output\"]\n    )\n    def test_main_exit_with_results(self):\n        # Test that bandit exits when there are results\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        with mock.patch(\n            \"bandit.core.manager.BanditManager.results_count\"\n        ) as mock_mgr_results_ct:\n            mock_mgr_results_ct.return_value = 1\n            # assert a SystemExit with code 1\n            self.assertRaisesRegex(SystemExit, \"1\", bandit.main)\n\n    @mock.patch(\n        \"sys.argv\", [\"bandit\", \"-c\", \"bandit.yaml\", \"test\", \"-o\", \"output\"]\n    )\n    def test_main_exit_with_no_results(self):\n        # Test that bandit exits when there are no results\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        with mock.patch(\n            \"bandit.core.manager.BanditManager.results_count\"\n        ) as mock_mgr_results_ct:\n            mock_mgr_results_ct.return_value = 0\n            # assert a SystemExit with code 0\n            self.assertRaisesRegex(SystemExit, \"0\", bandit.main)\n\n    @mock.patch(\n        \"sys.argv\",\n        [\"bandit\", \"-c\", \"bandit.yaml\", \"test\", \"-o\", \"output\", \"--exit-zero\"],\n    )\n    def test_main_exit_with_results_and_with_exit_zero_flag(self):\n        # Test that bandit exits with 0 on results and zero flag\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        os.chdir(temp_directory)\n        with open(\"bandit.yaml\", \"w\") as fd:\n            fd.write(bandit_config_content)\n        with mock.patch(\n            \"bandit.core.manager.BanditManager.results_count\"\n        ) as mock_mgr_results_ct:\n            mock_mgr_results_ct.return_value = 1\n\n            self.assertRaisesRegex(SystemExit, \"0\", bandit.main)\n"
  },
  {
    "path": "tests/unit/core/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/core/test_blacklisting.py",
    "content": "#\n# Copyright 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport testtools\n\nfrom bandit.core import blacklisting\n\n\nclass BlacklistingTests(testtools.TestCase):\n    def test_report_issue(self):\n        data = {\"level\": \"HIGH\", \"message\": \"test {name}\", \"id\": \"B000\"}\n\n        issue = blacklisting.report_issue(data, \"name\")\n        issue_dict = issue.as_dict(with_code=False)\n        self.assertIsInstance(issue_dict, dict)\n        self.assertEqual(\"B000\", issue_dict[\"test_id\"])\n        self.assertEqual(\"HIGH\", issue_dict[\"issue_severity\"])\n        self.assertEqual({}, issue_dict[\"issue_cwe\"])\n        self.assertEqual(\"HIGH\", issue_dict[\"issue_confidence\"])\n        self.assertEqual(\"test name\", issue_dict[\"issue_text\"])\n\n    def test_report_issue_defaults(self):\n        data = {\"message\": \"test {name}\"}\n\n        issue = blacklisting.report_issue(data, \"name\")\n        issue_dict = issue.as_dict(with_code=False)\n        self.assertIsInstance(issue_dict, dict)\n        self.assertEqual(\"LEGACY\", issue_dict[\"test_id\"])\n        self.assertEqual(\"MEDIUM\", issue_dict[\"issue_severity\"])\n        self.assertEqual({}, issue_dict[\"issue_cwe\"])\n        self.assertEqual(\"HIGH\", issue_dict[\"issue_confidence\"])\n        self.assertEqual(\"test name\", issue_dict[\"issue_text\"])\n"
  },
  {
    "path": "tests/unit/core/test_config.py",
    "content": "# Copyright 2015 IBM Corp.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\nimport tempfile\nimport textwrap\nimport uuid\nfrom unittest import mock\n\nimport fixtures\nimport testtools\n\nfrom bandit.core import config\nfrom bandit.core import utils\n\n\nclass TempFile(fixtures.Fixture):\n    def __init__(self, contents=None, suffix=\".yaml\"):\n        super().__init__()\n        self.contents = contents\n        self.suffix = suffix\n\n    def setUp(self):\n        super().setUp()\n\n        with tempfile.NamedTemporaryFile(\n            suffix=self.suffix, mode=\"wt\", delete=False\n        ) as f:\n            if self.contents:\n                f.write(self.contents)\n\n        self.addCleanup(os.unlink, f.name)\n\n        self.name = f.name\n\n\nclass TestInit(testtools.TestCase):\n    def test_settings(self):\n        # Can initialize a BanditConfig.\n\n        example_key = uuid.uuid4().hex\n        example_value = self.getUniqueString()\n        contents = f\"{example_key}: {example_value}\"\n        f = self.useFixture(TempFile(contents))\n        b_config = config.BanditConfig(f.name)\n\n        # After initialization, can get settings.\n        self.assertEqual(\"*.py\", b_config.get_setting(\"plugin_name_pattern\"))\n\n        self.assertEqual({example_key: example_value}, b_config.config)\n        self.assertEqual(example_value, b_config.get_option(example_key))\n\n    def test_file_does_not_exist(self):\n        # When the config file doesn't exist, ConfigFileUnopenable is raised.\n\n        cfg_file = os.path.join(os.getcwd(), \"notafile\")\n        self.assertRaisesRegex(\n            utils.ConfigError, cfg_file, config.BanditConfig, cfg_file\n        )\n\n    def test_yaml_invalid(self):\n        # When the config yaml file isn't valid, sys.exit(2) is called.\n\n        # The following is invalid because it starts a sequence and doesn't\n        # end it.\n        invalid_yaml = \"- [ something\"\n        f = self.useFixture(TempFile(invalid_yaml))\n        self.assertRaisesRegex(\n            utils.ConfigError, f.name, config.BanditConfig, f.name\n        )\n\n\nclass TestGetOption(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n\n        self.example_key = uuid.uuid4().hex\n        self.example_subkey = uuid.uuid4().hex\n        self.example_subvalue = uuid.uuid4().hex\n        sample_yaml = textwrap.dedent(\n            f\"\"\"\n            {self.example_key}:\n                {self.example_subkey}: {self.example_subvalue}\n            \"\"\"\n        )\n\n        f = self.useFixture(TempFile(sample_yaml))\n\n        self.b_config = config.BanditConfig(f.name)\n\n    def test_levels(self):\n        # get_option with .-separated string.\n\n        sample_option_name = f\"{self.example_key}.{self.example_subkey}\"\n        self.assertEqual(\n            self.example_subvalue, self.b_config.get_option(sample_option_name)\n        )\n\n    def test_levels_not_exist(self):\n        # get_option when option name doesn't exist returns None.\n\n        sample_option_name = f\"{uuid.uuid4().hex}.{uuid.uuid4().hex}\"\n        self.assertIsNone(self.b_config.get_option(sample_option_name))\n\n\nclass TestGetSetting(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        test_yaml = \"key: value\"\n        f = self.useFixture(TempFile(test_yaml))\n        self.b_config = config.BanditConfig(f.name)\n\n    def test_not_exist(self):\n        # get_setting() when the name doesn't exist returns None\n\n        sample_setting_name = uuid.uuid4().hex\n        self.assertIsNone(self.b_config.get_setting(sample_setting_name))\n\n\nclass TestConfigCompat(testtools.TestCase):\n    sample = textwrap.dedent(\n        \"\"\"\n        profiles:\n            test_1:\n                include:\n                    - any_other_function_with_shell_equals_true\n                    - assert_used\n                exclude:\n\n            test_2:\n                include:\n                    - blacklist_calls\n\n            test_3:\n                include:\n                    - blacklist_imports\n\n            test_4:\n                exclude:\n                    - assert_used\n\n            test_5:\n                exclude:\n                    - blacklist_calls\n                    - blacklist_imports\n\n            test_6:\n                include:\n                    - blacklist_calls\n\n                exclude:\n                    - blacklist_imports\n\n        blacklist_calls:\n            bad_name_sets:\n                - pickle:\n                    qualnames: [pickle.loads]\n                    message: \"{func} library appears to be in use.\"\n\n        blacklist_imports:\n            bad_import_sets:\n                - telnet:\n                    imports: [telnetlib]\n                    level: HIGH\n                    message: \"{module} is considered insecure.\"\n        \"\"\"\n    )\n    suffix = \".yaml\"\n\n    def setUp(self):\n        super().setUp()\n        f = self.useFixture(TempFile(self.sample, suffix=self.suffix))\n        self.config = config.BanditConfig(f.name)\n\n    def test_converted_include(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_1\"]\n        data = {\n            \"blacklist\": {},\n            \"exclude\": set(),\n            \"include\": {\"B101\", \"B604\"},\n        }\n\n        self.assertEqual(data, test)\n\n    def test_converted_exclude(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_4\"]\n\n        self.assertEqual({\"B101\"}, test[\"exclude\"])\n\n    def test_converted_blacklist_call_data(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_2\"]\n        data = {\n            \"Call\": [\n                {\n                    \"qualnames\": [\"telnetlib\"],\n                    \"level\": \"HIGH\",\n                    \"message\": \"{name} is considered insecure.\",\n                    \"name\": \"telnet\",\n                }\n            ]\n        }\n\n        self.assertEqual(data, test[\"blacklist\"])\n\n    def test_converted_blacklist_import_data(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_3\"]\n        data = [\n            {\n                \"message\": \"{name} library appears to be in use.\",\n                \"name\": \"pickle\",\n                \"qualnames\": [\"pickle.loads\"],\n            }\n        ]\n\n        self.assertEqual(data, test[\"blacklist\"][\"Call\"])\n        self.assertEqual(data, test[\"blacklist\"][\"Import\"])\n        self.assertEqual(data, test[\"blacklist\"][\"ImportFrom\"])\n\n    def test_converted_blacklist_call_test(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_2\"]\n\n        self.assertEqual({\"B001\"}, test[\"include\"])\n\n    def test_converted_blacklist_import_test(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_3\"]\n\n        self.assertEqual({\"B001\"}, test[\"include\"])\n\n    def test_converted_exclude_blacklist(self):\n        profiles = self.config.get_option(\"profiles\")\n        test = profiles[\"test_5\"]\n\n        self.assertEqual({\"B001\"}, test[\"exclude\"])\n\n    def test_deprecation_message(self):\n        msg = (\n            \"Config file '%s' contains deprecated legacy config data. \"\n            \"Please consider upgrading to the new config format. The tool \"\n            \"'bandit-config-generator' can help you with this. Support for \"\n            \"legacy configs will be removed in a future bandit version.\"\n        )\n\n        with mock.patch(\"bandit.core.config.LOG.warning\") as m:\n            self.config._config = {\"profiles\": {}}\n            self.config.validate(\"\")\n            self.assertEqual((msg, \"\"), m.call_args_list[0][0])\n\n    def test_blacklist_error(self):\n        msg = (\n            \" : Config file has an include or exclude reference to legacy \"\n            \"test '%s' but no configuration data for it. Configuration \"\n            \"data is required for this test. Please consider switching to \"\n            \"the new config file format, the tool \"\n            \"'bandit-config-generator' can help you with this.\"\n        )\n\n        for name in [\n            \"blacklist_call\",\n            \"blacklist_imports\",\n            \"blacklist_imports_func\",\n        ]:\n            self.config._config = {\"profiles\": {\"test\": {\"include\": [name]}}}\n            try:\n                self.config.validate(\"\")\n            except utils.ConfigError as e:\n                self.assertEqual(msg % name, e.message)\n\n    def test_bad_yaml(self):\n        f = self.useFixture(TempFile(\"[]\"))\n        try:\n            self.config = config.BanditConfig(f.name)\n        except utils.ConfigError as e:\n            self.assertIn(\"Error parsing file.\", e.message)\n\n\nclass TestTomlConfig(TestConfigCompat):\n    sample = textwrap.dedent(\n        \"\"\"\n        [tool.bandit.profiles.test_1]\n        include = [\n            \"any_other_function_with_shell_equals_true\",\n            \"assert_used\",\n        ]\n\n        [tool.bandit.profiles.test_2]\n        include = [\"blacklist_calls\"]\n\n        [tool.bandit.profiles.test_3]\n        include = [\"blacklist_imports\"]\n\n        [tool.bandit.profiles.test_4]\n        exclude = [\"assert_used\"]\n\n        [tool.bandit.profiles.test_5]\n        exclude = [\"blacklist_calls\", \"blacklist_imports\"]\n\n        [tool.bandit.profiles.test_6]\n        include = [\"blacklist_calls\"]\n        exclude = [\"blacklist_imports\"]\n\n        [[tool.bandit.blacklist_calls.bad_name_sets]]\n            [tool.bandit.blacklist_calls.bad_name_sets.pickle]\n            qualnames = [\"pickle.loads\"]\n            message = \"{func} library appears to be in use.\"\n\n        [[tool.bandit.blacklist_imports.bad_import_sets]]\n            [tool.bandit.blacklist_imports.bad_import_sets.telnet]\n            imports = [\"telnetlib\"]\n            level = \"HIGH\"\n            message = \"{module} is considered insecure.\"\n        \"\"\"\n    )\n    suffix = \".toml\"\n"
  },
  {
    "path": "tests/unit/core/test_context.py",
    "content": "#\n# Copyright 2015 Red Hat, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\nfrom unittest import mock\n\nimport testtools\n\nfrom bandit.core import context\n\n\nclass ContextTests(testtools.TestCase):\n    def test_context_create(self):\n        ref_context = mock.Mock()\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(ref_context, new_context._context)\n\n        new_context = context.Context()\n        self.assertIsInstance(new_context._context, dict)\n\n    def test_repr(self):\n        ref_object = dict(spam=\"eggs\")\n        expected_repr = f\"<Context {ref_object}>\"\n        new_context = context.Context(context_object=ref_object)\n        self.assertEqual(expected_repr, repr(new_context))\n\n    @mock.patch(\"bandit.core.context.Context._get_literal_value\")\n    def test_call_args(self, get_literal_value):\n        get_literal_value.return_value = \"eggs\"\n        ref_call = mock.Mock()\n        ref_call.args = [mock.Mock(attr=\"spam\"), \"eggs\"]\n        ref_context = dict(call=ref_call)\n        new_context = context.Context(context_object=ref_context)\n        expected_args = [\"spam\", \"eggs\"]\n        self.assertListEqual(expected_args, new_context.call_args)\n\n    def test_call_args_count(self):\n        ref_call = mock.Mock()\n        ref_call.args = [\"spam\", \"eggs\"]\n        ref_context = dict(call=ref_call)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(len(ref_call.args), new_context.call_args_count)\n\n        ref_context = dict(call={})\n        new_context = context.Context(context_object=ref_context)\n        self.assertIsNone(new_context.call_args_count)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.call_args_count)\n\n    def test_call_function_name(self):\n        expected_string = \"spam\"\n        ref_context = dict(name=expected_string)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(expected_string, new_context.call_function_name)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.call_function_name)\n\n    def test_call_function_name_qual(self):\n        expected_string = \"spam\"\n        ref_context = dict(qualname=expected_string)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(expected_string, new_context.call_function_name_qual)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.call_function_name_qual)\n\n    @mock.patch(\"bandit.core.context.Context._get_literal_value\")\n    def test_call_keywords(self, get_literal_value):\n        get_literal_value.return_value = \"eggs\"\n        ref_keyword1 = mock.Mock(arg=\"arg1\", value=mock.Mock(attr=\"spam\"))\n        ref_keyword2 = mock.Mock(arg=\"arg2\", value=\"eggs\")\n        ref_call = mock.Mock()\n        ref_call.keywords = [ref_keyword1, ref_keyword2]\n        ref_context = dict(call=ref_call)\n        new_context = context.Context(context_object=ref_context)\n        expected_dict = dict(arg1=\"spam\", arg2=\"eggs\")\n        self.assertDictEqual(expected_dict, new_context.call_keywords)\n\n        ref_context = dict(call=None)\n        new_context = context.Context(context_object=ref_context)\n        self.assertIsNone(new_context.call_keywords)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.call_keywords)\n\n    def test_node(self):\n        expected_node = \"spam\"\n        ref_context = dict(node=expected_node)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(expected_node, new_context.node)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.node)\n\n    def test_string_val(self):\n        expected_string = \"spam\"\n        ref_context = dict(str=expected_string)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(expected_string, new_context.string_val)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.string_val)\n\n    def test_statement(self):\n        expected_string = \"spam\"\n        ref_context = dict(statement=expected_string)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(expected_string, new_context.statement)\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.statement)\n\n    @mock.patch(\"bandit.core.utils.get_qual_attr\")\n    def test_function_def_defaults_qual(self, get_qual_attr):\n        get_qual_attr.return_value = \"spam\"\n        ref_node = mock.Mock(args=mock.Mock(defaults=[\"spam\"]))\n        ref_context = dict(node=ref_node, import_aliases=None)\n        new_context = context.Context(context_object=ref_context)\n        self.assertListEqual([\"spam\"], new_context.function_def_defaults_qual)\n\n        ref_node = mock.Mock(args=mock.Mock(defaults=[]))\n        ref_context = dict(node=ref_node, import_aliases=None)\n        new_context = context.Context(context_object=ref_context)\n        self.assertListEqual([], new_context.function_def_defaults_qual)\n\n        new_context = context.Context()\n        self.assertListEqual([], new_context.function_def_defaults_qual)\n\n    def test__get_literal_value(self):\n        new_context = context.Context()\n\n        value = ast.Constant(42)\n        expected = value.value\n        self.assertEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.Constant(\"spam\")\n        expected = value.value\n        self.assertEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.List([ast.Constant(\"spam\"), ast.Constant(42)], ast.Load())\n        expected = [ast.Constant(\"spam\").value, ast.Constant(42).value]\n        self.assertListEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.Tuple([ast.Constant(\"spam\"), ast.Constant(42)], ast.Load())\n        expected = (ast.Constant(\"spam\").value, ast.Constant(42).value)\n        self.assertTupleEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.Set([ast.Constant(\"spam\"), ast.Constant(42)])\n        expected = {ast.Constant(\"spam\").value, ast.Constant(42).value}\n        self.assertSetEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.Dict([\"spam\", \"eggs\"], [42, \"foo\"])\n        expected = dict(spam=42, eggs=\"foo\")\n        self.assertDictEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.Name(\"spam\", ast.Load())\n        expected = value.id\n        self.assertEqual(expected, new_context._get_literal_value(value))\n\n        value = ast.Constant(b\"spam\")\n        expected = value.value\n        self.assertEqual(expected, new_context._get_literal_value(value))\n\n        self.assertIsNone(new_context._get_literal_value(None))\n\n    @mock.patch(\n        \"bandit.core.context.Context.call_keywords\",\n        new_callable=mock.PropertyMock,\n    )\n    def test_check_call_arg_value(self, call_keywords):\n        new_context = context.Context()\n        call_keywords.return_value = dict(spam=\"eggs\")\n        self.assertTrue(new_context.check_call_arg_value(\"spam\", \"eggs\"))\n        self.assertTrue(\n            new_context.check_call_arg_value(\"spam\", [\"spam\", \"eggs\"])\n        )\n        self.assertFalse(new_context.check_call_arg_value(\"spam\", \"spam\"))\n        self.assertFalse(new_context.check_call_arg_value(\"spam\"))\n        self.assertFalse(new_context.check_call_arg_value(\"eggs\"))\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.check_call_arg_value(None))\n\n    @mock.patch(\n        \"bandit.core.context.Context.node\", new_callable=mock.PropertyMock\n    )\n    def test_get_lineno_for_call_arg(self, node):\n        expected_lineno = 42\n        keyword1 = mock.Mock(\n            arg=\"spam\", value=mock.Mock(lineno=expected_lineno)\n        )\n        node.return_value = mock.Mock(keywords=[keyword1])\n        new_context = context.Context()\n        actual_lineno = new_context.get_lineno_for_call_arg(\"spam\")\n        self.assertEqual(expected_lineno, actual_lineno)\n\n        new_context = context.Context()\n        missing_lineno = new_context.get_lineno_for_call_arg(\"eggs\")\n        self.assertIsNone(missing_lineno)\n\n    def test_get_call_arg_at_position(self):\n        expected_arg = \"spam\"\n        ref_call = mock.Mock()\n        ref_call.args = [ast.Constant(expected_arg)]\n        ref_context = dict(call=ref_call)\n        new_context = context.Context(context_object=ref_context)\n        self.assertEqual(expected_arg, new_context.get_call_arg_at_position(0))\n        self.assertIsNone(new_context.get_call_arg_at_position(1))\n\n        ref_call = mock.Mock()\n        ref_call.args = []\n        ref_context = dict(call=ref_call)\n        new_context = context.Context(context_object=ref_context)\n        self.assertIsNone(new_context.get_call_arg_at_position(0))\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.get_call_arg_at_position(0))\n\n    def test_is_module_being_imported(self):\n        ref_context = dict(module=\"spam\")\n        new_context = context.Context(context_object=ref_context)\n        self.assertTrue(new_context.is_module_being_imported(\"spam\"))\n        self.assertFalse(new_context.is_module_being_imported(\"eggs\"))\n\n        new_context = context.Context()\n        self.assertFalse(new_context.is_module_being_imported(\"spam\"))\n\n    def test_is_module_imported_exact(self):\n        ref_context = dict(imports=[\"spam\"])\n        new_context = context.Context(context_object=ref_context)\n        self.assertTrue(new_context.is_module_imported_exact(\"spam\"))\n        self.assertFalse(new_context.is_module_imported_exact(\"eggs\"))\n\n        new_context = context.Context()\n        self.assertFalse(new_context.is_module_being_imported(\"spam\"))\n\n    def test_is_module_imported_like(self):\n        ref_context = dict(imports=[[\"spam\"], [\"eggs\"]])\n        new_context = context.Context(context_object=ref_context)\n        self.assertTrue(new_context.is_module_imported_like(\"spam\"))\n        self.assertFalse(new_context.is_module_imported_like(\"bacon\"))\n\n        new_context = context.Context()\n        self.assertFalse(new_context.is_module_imported_like(\"spam\"))\n\n    def test_filename(self):\n        ref_context = dict(filename=\"spam.py\")\n        new_context = context.Context(context_object=ref_context)\n\n        self.assertEqual(new_context.filename, \"spam.py\")\n\n        new_context = context.Context()\n        self.assertIsNone(new_context.filename)\n"
  },
  {
    "path": "tests/unit/core/test_docs_util.py",
    "content": "# Copyright 2019 Victor Torre\n#\n# SPDX-License-Identifier: Apache-2.0\nimport testtools\n\nimport bandit\nfrom bandit.core.docs_utils import get_url\n\n\nclass DocsUtilTests(testtools.TestCase):\n    \"\"\"This set of tests exercises bandit.core.docs_util functions.\"\"\"\n\n    BASE_URL = f\"https://bandit.readthedocs.io/en/{bandit.__version__}/\"\n\n    def test_overwrite_bib_info(self):\n        expected_url = self.BASE_URL + (\n            \"blacklists/blacklist_calls.html\" \"#b304-b305-ciphers-and-modes\"\n        )\n        self.assertEqual(get_url(\"B304\"), get_url(\"B305\"))\n        self.assertEqual(expected_url, get_url(\"B304\"))\n\n    def test_plugin_call_bib(self):\n        expected_url = self.BASE_URL + \"plugins/b101_assert_used.html\"\n        self.assertEqual(expected_url, get_url(\"B101\"))\n\n    def test_import_call_bib(self):\n        expected_url = self.BASE_URL + (\n            \"blacklists/blacklist_imports.html\" \"#b413-import-pycrypto\"\n        )\n        self.assertEqual(expected_url, get_url(\"B413\"))\n"
  },
  {
    "path": "tests/unit/core/test_issue.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nfrom unittest import mock\n\nimport testtools\n\nimport bandit\nfrom bandit.core import constants\nfrom bandit.core import issue\n\n\nclass IssueTests(testtools.TestCase):\n    def test_issue_create(self):\n        new_issue = _get_issue_instance()\n        self.assertIsInstance(new_issue, issue.Issue)\n\n    def test_issue_str(self):\n        test_issue = _get_issue_instance()\n        expect = (\n            \"Issue: 'Test issue' from B999:bandit_plugin:\"\n            \" CWE: %s,\"\n            \" Severity: MEDIUM \"\n            \"Confidence: MEDIUM at code.py:1:8\"\n        )\n\n        self.assertEqual(\n            expect % str(issue.Cwe(issue.Cwe.MULTIPLE_BINDS)), str(test_issue)\n        )\n\n    def test_issue_as_dict(self):\n        test_issue = _get_issue_instance()\n        test_issue_dict = test_issue.as_dict(with_code=False)\n        self.assertIsInstance(test_issue_dict, dict)\n        self.assertEqual(\"code.py\", test_issue_dict[\"filename\"])\n        self.assertEqual(\"bandit_plugin\", test_issue_dict[\"test_name\"])\n        self.assertEqual(\"B999\", test_issue_dict[\"test_id\"])\n        self.assertEqual(\"MEDIUM\", test_issue_dict[\"issue_severity\"])\n        self.assertEqual(\n            {\n                \"id\": 605,\n                \"link\": \"https://cwe.mitre.org/data/definitions/605.html\",\n            },\n            test_issue_dict[\"issue_cwe\"],\n        )\n        self.assertEqual(\"MEDIUM\", test_issue_dict[\"issue_confidence\"])\n        self.assertEqual(\"Test issue\", test_issue_dict[\"issue_text\"])\n        self.assertEqual(1, test_issue_dict[\"line_number\"])\n        self.assertEqual([], test_issue_dict[\"line_range\"])\n        self.assertEqual(8, test_issue_dict[\"col_offset\"])\n        self.assertEqual(16, test_issue_dict[\"end_col_offset\"])\n\n    def test_issue_filter_severity(self):\n        levels = [bandit.LOW, bandit.MEDIUM, bandit.HIGH]\n        issues = [_get_issue_instance(level, bandit.HIGH) for level in levels]\n\n        for level in levels:\n            rank = constants.RANKING.index(level)\n            for i in issues:\n                test = constants.RANKING.index(i.severity)\n                result = i.filter(level, bandit.UNDEFINED)\n                self.assertTrue((test >= rank) == result)\n\n    def test_issue_filter_confidence(self):\n        levels = [bandit.LOW, bandit.MEDIUM, bandit.HIGH]\n        issues = [_get_issue_instance(bandit.HIGH, level) for level in levels]\n\n        for level in levels:\n            rank = constants.RANKING.index(level)\n            for i in issues:\n                test = constants.RANKING.index(i.confidence)\n                result = i.filter(bandit.UNDEFINED, level)\n                self.assertTrue((test >= rank) == result)\n\n    def test_matches_issue(self):\n        issue_a = _get_issue_instance()\n\n        issue_b = _get_issue_instance(severity=bandit.HIGH)\n\n        issue_c = _get_issue_instance(confidence=bandit.LOW)\n\n        issue_d = _get_issue_instance()\n        issue_d.text = \"ABCD\"\n\n        issue_e = _get_issue_instance()\n        issue_e.fname = \"file1.py\"\n\n        issue_f = issue_a\n\n        issue_g = _get_issue_instance()\n        issue_g.test = \"ZZZZ\"\n\n        issue_h = issue_a\n        issue_h.lineno = 12345\n\n        # positive tests\n        self.assertEqual(issue_a, issue_a)\n        self.assertEqual(issue_a, issue_f)\n        self.assertEqual(issue_f, issue_a)\n\n        # severity doesn't match\n        self.assertNotEqual(issue_a, issue_b)\n\n        # confidence doesn't match\n        self.assertNotEqual(issue_a, issue_c)\n\n        # text doesn't match\n        self.assertNotEqual(issue_a, issue_d)\n\n        # filename doesn't match\n        self.assertNotEqual(issue_a, issue_e)\n\n        # plugin name doesn't match\n        self.assertNotEqual(issue_a, issue_g)\n\n        # line number doesn't match but should pass because we don't test that\n        self.assertEqual(issue_a, issue_h)\n\n    @mock.patch(\"linecache.getline\")\n    def test_get_code(self, getline):\n        getline.return_value = b\"\\x08\\x30\"\n        new_issue = issue.Issue(\n            bandit.MEDIUM, cwe=issue.Cwe.MULTIPLE_BINDS, lineno=1\n        )\n\n        try:\n            new_issue.get_code()\n        except UnicodeDecodeError:\n            self.fail(\"Bytes not properly decoded in issue.get_code()\")\n\n\ndef _get_issue_instance(\n    severity=bandit.MEDIUM,\n    cwe=issue.Cwe.MULTIPLE_BINDS,\n    confidence=bandit.MEDIUM,\n):\n    new_issue = issue.Issue(severity, cwe, confidence, \"Test issue\")\n    new_issue.fname = \"code.py\"\n    new_issue.test = \"bandit_plugin\"\n    new_issue.test_id = \"B999\"\n    new_issue.lineno = 1\n    new_issue.col_offset = 8\n    new_issue.end_col_offset = 16\n\n    return new_issue\n"
  },
  {
    "path": "tests/unit/core/test_manager.py",
    "content": "#\n# Copyright 2015 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport os\nfrom unittest import mock\n\nimport fixtures\nimport testtools\n\nfrom bandit.core import config\nfrom bandit.core import constants\nfrom bandit.core import issue\nfrom bandit.core import manager\n\n\nclass ManagerTests(testtools.TestCase):\n    def _get_issue_instance(\n        self,\n        sev=constants.MEDIUM,\n        cwe=issue.Cwe.MULTIPLE_BINDS,\n        conf=constants.MEDIUM,\n    ):\n        new_issue = issue.Issue(sev, cwe, conf, \"Test issue\")\n        new_issue.fname = \"code.py\"\n        new_issue.test = \"bandit_plugin\"\n        new_issue.lineno = 1\n        return new_issue\n\n    def setUp(self):\n        super().setUp()\n        self.profile = {}\n        self.profile[\"include\"] = {\n            \"any_other_function_with_shell_equals_true\",\n            \"assert_used\",\n        }\n\n        self.config = config.BanditConfig()\n        self.manager = manager.BanditManager(\n            config=self.config, agg_type=\"file\", debug=False, verbose=False\n        )\n\n    def test_create_manager(self):\n        # make sure we can create a manager\n        self.assertEqual(False, self.manager.debug)\n        self.assertEqual(False, self.manager.verbose)\n        self.assertEqual(\"file\", self.manager.agg_type)\n\n    def test_create_manager_with_profile(self):\n        # make sure we can create a manager\n        m = manager.BanditManager(\n            config=self.config,\n            agg_type=\"file\",\n            debug=False,\n            verbose=False,\n            profile=self.profile,\n        )\n\n        self.assertEqual(False, m.debug)\n        self.assertEqual(False, m.verbose)\n        self.assertEqual(\"file\", m.agg_type)\n\n    def test_matches_globlist(self):\n        self.assertTrue(manager._matches_glob_list(\"test\", [\"*tes*\"]))\n        self.assertFalse(manager._matches_glob_list(\"test\", [\"*fes*\"]))\n\n    def test_is_file_included(self):\n        a = manager._is_file_included(\n            path=\"a.py\",\n            included_globs=[\"*.py\"],\n            excluded_path_strings=[],\n            enforce_glob=True,\n        )\n\n        b = manager._is_file_included(\n            path=\"a.dd\",\n            included_globs=[\"*.py\"],\n            excluded_path_strings=[],\n            enforce_glob=False,\n        )\n\n        c = manager._is_file_included(\n            path=\"a.py\",\n            included_globs=[\"*.py\"],\n            excluded_path_strings=[\"a.py\"],\n            enforce_glob=True,\n        )\n\n        d = manager._is_file_included(\n            path=\"a.dd\",\n            included_globs=[\"*.py\"],\n            excluded_path_strings=[],\n            enforce_glob=True,\n        )\n\n        e = manager._is_file_included(\n            path=\"x_a.py\",\n            included_globs=[\"*.py\"],\n            excluded_path_strings=[\"x_*.py\"],\n            enforce_glob=True,\n        )\n\n        f = manager._is_file_included(\n            path=\"x.py\",\n            included_globs=[\"*.py\"],\n            excluded_path_strings=[\"x_*.py\"],\n            enforce_glob=True,\n        )\n        self.assertTrue(a)\n        self.assertTrue(b)\n        self.assertFalse(c)\n        self.assertFalse(d)\n        self.assertFalse(e)\n        self.assertTrue(f)\n\n    @mock.patch(\"os.walk\")\n    def test_get_files_from_dir(self, os_walk):\n        os_walk.return_value = [\n            (\"/\", (\"a\"), ()),\n            (\"/a\", (), (\"a.py\", \"b.py\", \"c.ww\")),\n        ]\n\n        inc, exc = manager._get_files_from_dir(\n            files_dir=\"\", included_globs=[\"*.py\"], excluded_path_strings=None\n        )\n\n        self.assertEqual({\"/a/c.ww\"}, exc)\n        self.assertEqual({\"/a/a.py\", \"/a/b.py\"}, inc)\n\n    def test_populate_baseline_success(self):\n        # Test populate_baseline with valid JSON\n        baseline_data = \"\"\"{\n            \"results\": [\n                {\n                    \"code\": \"test code\",\n                    \"filename\": \"example_file.py\",\n                    \"issue_severity\": \"low\",\n                    \"issue_cwe\": {\n                        \"id\": 605,\n                        \"link\": \"%s\"\n                    },\n                    \"issue_confidence\": \"low\",\n                    \"issue_text\": \"test issue\",\n                    \"test_name\": \"some_test\",\n                    \"test_id\": \"x\",\n                    \"line_number\": \"n\",\n                    \"line_range\": \"n-m\"\n                }\n            ]\n        }\n        \"\"\" % (\n            \"https://cwe.mitre.org/data/definitions/605.html\"\n        )\n        issue_dictionary = {\n            \"code\": \"test code\",\n            \"filename\": \"example_file.py\",\n            \"issue_severity\": \"low\",\n            \"issue_cwe\": issue.Cwe(issue.Cwe.MULTIPLE_BINDS).as_dict(),\n            \"issue_confidence\": \"low\",\n            \"issue_text\": \"test issue\",\n            \"test_name\": \"some_test\",\n            \"test_id\": \"x\",\n            \"line_number\": \"n\",\n            \"line_range\": \"n-m\",\n        }\n        baseline_items = [issue.issue_from_dict(issue_dictionary)]\n        self.manager.populate_baseline(baseline_data)\n        self.assertEqual(baseline_items, self.manager.baseline)\n\n    @mock.patch(\"logging.Logger.warning\")\n    def test_populate_baseline_invalid_json(self, mock_logger_warning):\n        # Test populate_baseline with invalid JSON content\n        baseline_data = \"\"\"{\"data\": \"bad\"}\"\"\"\n        self.manager.populate_baseline(baseline_data)\n        # Default value for manager.baseline is []\n        self.assertEqual([], self.manager.baseline)\n        self.assertTrue(mock_logger_warning.called)\n\n    def test_results_count(self):\n        levels = [constants.LOW, constants.MEDIUM, constants.HIGH]\n        self.manager.results = [\n            issue.Issue(\n                severity=level, cwe=issue.Cwe.MULTIPLE_BINDS, confidence=level\n            )\n            for level in levels\n        ]\n\n        r = [\n            self.manager.results_count(sev_filter=level, conf_filter=level)\n            for level in levels\n        ]\n\n        self.assertEqual([3, 2, 1], r)\n\n    def test_output_results_invalid_format(self):\n        # Test that output_results succeeds given an invalid format\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        lines = 5\n        sev_level = constants.LOW\n        conf_level = constants.LOW\n        output_filename = os.path.join(temp_directory, \"_temp_output\")\n        output_format = \"invalid\"\n        with open(output_filename, \"w\") as tmp_file:\n            self.manager.output_results(\n                lines, sev_level, conf_level, tmp_file, output_format\n            )\n        self.assertTrue(os.path.isfile(output_filename))\n\n    def test_output_results_valid_format(self):\n        # Test that output_results succeeds given a valid format\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        lines = 5\n        sev_level = constants.LOW\n        conf_level = constants.LOW\n        output_filename = os.path.join(temp_directory, \"_temp_output.txt\")\n        output_format = \"txt\"\n        with open(output_filename, \"w\") as tmp_file:\n            self.manager.output_results(\n                lines, sev_level, conf_level, tmp_file, output_format\n            )\n        self.assertTrue(os.path.isfile(output_filename))\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_recurse_skip(self, isdir):\n        isdir.return_value = True\n        self.manager.discover_files([\"thing\"], False)\n        self.assertEqual([], self.manager.files_list)\n        self.assertEqual([], self.manager.excluded_files)\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_recurse_files(self, isdir):\n        isdir.return_value = True\n        with mock.patch.object(manager, \"_get_files_from_dir\") as m:\n            m.return_value = ({\"files\"}, {\"excluded\"})\n            self.manager.discover_files([\"thing\"], True)\n            self.assertEqual([\"files\"], self.manager.files_list)\n            self.assertEqual([\"excluded\"], self.manager.excluded_files)\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_exclude(self, isdir):\n        isdir.return_value = False\n        with mock.patch.object(manager, \"_is_file_included\") as m:\n            m.return_value = False\n            self.manager.discover_files([\"thing\"], True)\n            self.assertEqual([], self.manager.files_list)\n            self.assertEqual([\"thing\"], self.manager.excluded_files)\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_exclude_dir(self, isdir):\n        isdir.return_value = False\n\n        # Test exclude dir using wildcard\n        self.manager.discover_files([\"./x/y.py\"], True, \"./x/*\")\n        self.assertEqual([], self.manager.files_list)\n        self.assertEqual([\"./x/y.py\"], self.manager.excluded_files)\n\n        # Test exclude dir without wildcard\n        isdir.side_effect = [True, False]\n        self.manager.discover_files([\"./x/y.py\"], True, \"./x/\")\n        self.assertEqual([], self.manager.files_list)\n        self.assertEqual([\"./x/y.py\"], self.manager.excluded_files)\n\n        # Test exclude dir without wildcard or trailing slash\n        isdir.side_effect = [True, False]\n        self.manager.discover_files([\"./x/y.py\"], True, \"./x\")\n        self.assertEqual([], self.manager.files_list)\n        self.assertEqual([\"./x/y.py\"], self.manager.excluded_files)\n\n        # Test exclude dir without prefix or suffix\n        isdir.side_effect = [False, False]\n        self.manager.discover_files([\"./x/y/z.py\"], True, \"y\")\n        self.assertEqual([], self.manager.files_list)\n        self.assertEqual([\"./x/y/z.py\"], self.manager.excluded_files)\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_exclude_cmdline(self, isdir):\n        isdir.return_value = False\n        with mock.patch.object(manager, \"_is_file_included\") as m:\n            self.manager.discover_files(\n                [\"a\", \"b\", \"c\"], True, excluded_paths=\"a,b\"\n            )\n            m.assert_called_with(\n                \"c\", [\"*.py\", \"*.pyw\"], [\"a\", \"b\"], enforce_glob=False\n            )\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_exclude_glob(self, isdir):\n        isdir.return_value = False\n        self.manager.discover_files(\n            [\"a.py\", \"test_a.py\", \"test.py\"], True, excluded_paths=\"test_*.py\"\n        )\n        self.assertEqual([\"./a.py\", \"./test.py\"], self.manager.files_list)\n        self.assertEqual([\"test_a.py\"], self.manager.excluded_files)\n\n    @mock.patch(\"os.path.isdir\")\n    def test_discover_files_include(self, isdir):\n        isdir.return_value = False\n        with mock.patch.object(manager, \"_is_file_included\") as m:\n            m.return_value = True\n            self.manager.discover_files([\"thing\"], True)\n            self.assertEqual([\"./thing\"], self.manager.files_list)\n            self.assertEqual([], self.manager.excluded_files)\n\n    def test_run_tests_keyboardinterrupt(self):\n        # Test that bandit manager exits when there is a keyboard interrupt\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        some_file = os.path.join(temp_directory, \"some_code_file.py\")\n        with open(some_file, \"w\") as fd:\n            fd.write(\"some_code = x + 1\")\n        self.manager.files_list = [some_file]\n        with mock.patch(\n            \"bandit.core.metrics.Metrics.count_issues\"\n        ) as mock_count_issues:\n            mock_count_issues.side_effect = KeyboardInterrupt\n            # assert a SystemExit with code 2\n            self.assertRaisesRegex(SystemExit, \"2\", self.manager.run_tests)\n\n    def test_run_tests_ioerror(self):\n        # Test that a file name is skipped and added to the manager.skipped\n        # list when there is an IOError attempting to open/read the file\n        temp_directory = self.useFixture(fixtures.TempDir()).path\n        no_such_file = os.path.join(temp_directory, \"no_such_file.py\")\n        self.manager.files_list = [no_such_file]\n        self.manager.run_tests()\n        # since the file name and the IOError.strerror text are added to\n        # manager.skipped, we convert skipped to str to find just the file name\n        # since IOError is not constant\n        self.assertIn(no_such_file, str(self.manager.skipped))\n\n    def test_compare_baseline(self):\n        issue_a = self._get_issue_instance()\n        issue_a.fname = \"file1.py\"\n\n        issue_b = self._get_issue_instance()\n        issue_b.fname = \"file2.py\"\n\n        issue_c = self._get_issue_instance(sev=constants.HIGH)\n        issue_c.fname = \"file1.py\"\n\n        # issue c is in results, not in baseline\n        self.assertEqual(\n            [issue_c],\n            manager._compare_baseline_results(\n                [issue_a, issue_b], [issue_a, issue_b, issue_c]\n            ),\n        )\n\n        # baseline and results are the same\n        self.assertEqual(\n            [],\n            manager._compare_baseline_results(\n                [issue_a, issue_b, issue_c], [issue_a, issue_b, issue_c]\n            ),\n        )\n\n        # results are better than baseline\n        self.assertEqual(\n            [],\n            manager._compare_baseline_results(\n                [issue_a, issue_b, issue_c], [issue_a, issue_b]\n            ),\n        )\n\n    def test_find_candidate_matches(self):\n        issue_a = self._get_issue_instance()\n        issue_b = self._get_issue_instance()\n\n        issue_c = self._get_issue_instance()\n        issue_c.fname = \"file1.py\"\n\n        # issue a and b are the same, both should be returned as candidates\n        self.assertEqual(\n            {issue_a: [issue_a, issue_b]},\n            manager._find_candidate_matches([issue_a], [issue_a, issue_b]),\n        )\n\n        # issue a and c are different, only a should be returned\n        self.assertEqual(\n            {issue_a: [issue_a]},\n            manager._find_candidate_matches([issue_a], [issue_a, issue_c]),\n        )\n\n        # c doesn't match a, empty list should be returned\n        self.assertEqual(\n            {issue_a: []},\n            manager._find_candidate_matches([issue_a], [issue_c]),\n        )\n\n        # a and b match, a and b should both return a and b candidates\n        self.assertEqual(\n            {issue_a: [issue_a, issue_b], issue_b: [issue_a, issue_b]},\n            manager._find_candidate_matches(\n                [issue_a, issue_b], [issue_a, issue_b, issue_c]\n            ),\n        )\n"
  },
  {
    "path": "tests/unit/core/test_meta_ast.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport testtools\n\nfrom bandit.core import meta_ast\n\n\nclass BanditMetaAstTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        self.b_meta_ast = meta_ast.BanditMetaAst()\n        self.node = \"fake_node\"\n        self.parent_id = \"fake_parent_id\"\n        self.depth = 1\n        self.b_meta_ast.add_node(self.node, self.parent_id, self.depth)\n        self.node_id = hex(id(self.node))\n\n    def test_add_node(self):\n        expected = {\n            \"raw\": self.node,\n            \"parent_id\": self.parent_id,\n            \"depth\": self.depth,\n        }\n        self.assertEqual(expected, self.b_meta_ast.nodes[self.node_id])\n\n    def test_str(self):\n        node = self.b_meta_ast.nodes[self.node_id]\n        expected = f\"Node: {self.node_id}\\n\\t{node}\\nLength: 1\\n\"\n        self.assertEqual(expected, str(self.b_meta_ast))\n"
  },
  {
    "path": "tests/unit/core/test_test_set.py",
    "content": "#\n# Copyright (c) 2016 Hewlett-Packard Development Company, L.P.\n#\n# SPDX-License-Identifier: Apache-2.0\nfrom unittest import mock\n\nimport testtools\nfrom stevedore import extension\n\nfrom bandit.blacklists import utils\nfrom bandit.core import extension_loader\nfrom bandit.core import issue\nfrom bandit.core import test_properties as test\nfrom bandit.core import test_set\n\n\n@test.checks(\"Str\")\n@test.test_id(\"B000\")\ndef test_plugin():\n    sets = []\n    sets.append(\n        utils.build_conf_dict(\n            \"telnet\",\n            \"B401\",\n            issue.Cwe.CLEARTEXT_TRANSMISSION,\n            [\"telnetlib\"],\n            \"A telnet-related module is being imported.  Telnet is \"\n            \"considered insecure. Use SSH or some other encrypted protocol.\",\n            \"HIGH\",\n        )\n    )\n\n    sets.append(\n        utils.build_conf_dict(\n            \"marshal\",\n            \"B302\",\n            issue.Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA,\n            [\"marshal.load\", \"marshal.loads\"],\n            \"Deserialization with the marshal module is possibly dangerous.\",\n        )\n    )\n\n    return {\"Import\": sets, \"ImportFrom\": sets, \"Call\": sets}\n\n\nclass BanditTestSetTests(testtools.TestCase):\n    def _make_test_manager(self, plugin):\n        return extension.ExtensionManager.make_test_instance(\n            [extension.Extension(\"test_plugin\", None, test_plugin, None)]\n        )\n\n    def setUp(self):\n        super().setUp()\n        mngr = self._make_test_manager(mock.Mock)\n        self.patchExtMan = mock.patch(\"stevedore.extension.ExtensionManager\")\n        self.mockExtMan = self.patchExtMan.start()\n        self.mockExtMan.return_value = mngr\n        self.old_ext_man = extension_loader.MANAGER\n        extension_loader.MANAGER = extension_loader.Manager()\n        self.config = mock.MagicMock()\n        self.config.get_setting.return_value = None\n\n    def tearDown(self):\n        self.patchExtMan.stop()\n        super().tearDown()\n        extension_loader.MANAGER = self.old_ext_man\n\n    def test_has_defaults(self):\n        ts = test_set.BanditTestSet(self.config)\n        self.assertEqual(1, len(ts.get_tests(\"Str\")))\n\n    def test_profile_include_id(self):\n        profile = {\"include\": [\"B000\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n        self.assertEqual(1, len(ts.get_tests(\"Str\")))\n\n    def test_profile_exclude_id(self):\n        profile = {\"exclude\": [\"B000\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n        self.assertEqual(0, len(ts.get_tests(\"Str\")))\n\n    def test_profile_include_none(self):\n        profile = {\"include\": []}  # same as no include\n        ts = test_set.BanditTestSet(self.config, profile)\n        self.assertEqual(1, len(ts.get_tests(\"Str\")))\n\n    def test_profile_exclude_none(self):\n        profile = {\"exclude\": []}  # same as no exclude\n        ts = test_set.BanditTestSet(self.config, profile)\n        self.assertEqual(1, len(ts.get_tests(\"Str\")))\n\n    def test_profile_has_builtin_blacklist(self):\n        ts = test_set.BanditTestSet(self.config)\n        self.assertEqual(1, len(ts.get_tests(\"Import\")))\n        self.assertEqual(1, len(ts.get_tests(\"ImportFrom\")))\n        self.assertEqual(1, len(ts.get_tests(\"Call\")))\n\n    def test_profile_exclude_builtin_blacklist(self):\n        profile = {\"exclude\": [\"B001\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n        self.assertEqual(0, len(ts.get_tests(\"Import\")))\n        self.assertEqual(0, len(ts.get_tests(\"ImportFrom\")))\n        self.assertEqual(0, len(ts.get_tests(\"Call\")))\n\n    def test_profile_exclude_builtin_blacklist_specific(self):\n        profile = {\"exclude\": [\"B302\", \"B401\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n        self.assertEqual(0, len(ts.get_tests(\"Import\")))\n        self.assertEqual(0, len(ts.get_tests(\"ImportFrom\")))\n        self.assertEqual(0, len(ts.get_tests(\"Call\")))\n\n    def test_profile_filter_blacklist_none(self):\n        ts = test_set.BanditTestSet(self.config)\n        blacklist = ts.get_tests(\"Import\")[0]\n\n        self.assertEqual(2, len(blacklist._config[\"Import\"]))\n        self.assertEqual(2, len(blacklist._config[\"ImportFrom\"]))\n        self.assertEqual(2, len(blacklist._config[\"Call\"]))\n\n    def test_profile_filter_blacklist_one(self):\n        profile = {\"exclude\": [\"B401\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n        blacklist = ts.get_tests(\"Import\")[0]\n\n        self.assertEqual(1, len(blacklist._config[\"Import\"]))\n        self.assertEqual(1, len(blacklist._config[\"ImportFrom\"]))\n        self.assertEqual(1, len(blacklist._config[\"Call\"]))\n\n    def test_profile_filter_blacklist_include(self):\n        profile = {\"include\": [\"B001\", \"B401\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n        blacklist = ts.get_tests(\"Import\")[0]\n\n        self.assertEqual(1, len(blacklist._config[\"Import\"]))\n        self.assertEqual(1, len(blacklist._config[\"ImportFrom\"]))\n        self.assertEqual(1, len(blacklist._config[\"Call\"]))\n\n    def test_profile_filter_blacklist_all(self):\n        profile = {\"exclude\": [\"B401\", \"B302\"]}\n        ts = test_set.BanditTestSet(self.config, profile)\n\n        # if there is no blacklist data for a node type then we wont add a\n        # blacklist test to it, as this would be pointless.\n        self.assertEqual(0, len(ts.get_tests(\"Import\")))\n        self.assertEqual(0, len(ts.get_tests(\"ImportFrom\")))\n        self.assertEqual(0, len(ts.get_tests(\"Call\")))\n\n    def test_profile_blacklist_compat(self):\n        data = [\n            utils.build_conf_dict(\n                \"marshal\",\n                \"B302\",\n                issue.Cwe.DESERIALIZATION_OF_UNTRUSTED_DATA,\n                [\"marshal.load\", \"marshal.loads\"],\n                (\n                    \"Deserialization with the marshal module is possibly \"\n                    \"dangerous.\"\n                ),\n            )\n        ]\n\n        profile = {\"include\": [\"B001\"], \"blacklist\": {\"Call\": data}}\n\n        ts = test_set.BanditTestSet(self.config, profile)\n        blacklist = ts.get_tests(\"Call\")[0]\n\n        self.assertNotIn(\"Import\", blacklist._config)\n        self.assertNotIn(\"ImportFrom\", blacklist._config)\n        self.assertEqual(1, len(blacklist._config[\"Call\"]))\n"
  },
  {
    "path": "tests/unit/core/test_util.py",
    "content": "#\n# Copyright 2014 Hewlett-Packard Development Company, L.P.\n# Copyright 2015 Nebula, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport ast\nimport os\nimport shutil\nimport sys\nimport tempfile\n\nimport testtools\n\nfrom bandit.core import utils as b_utils\n\n\ndef _touch(path):\n    \"\"\"Create an empty file at ``path``.\"\"\"\n    open(path, \"w\").close()\n\n\nclass UtilTests(testtools.TestCase):\n    \"\"\"This set of tests exercises bandit.core.util functions.\"\"\"\n\n    def setUp(self):\n        super().setUp()\n        self._setup_get_module_qualname_from_path()\n\n    def _setup_get_module_qualname_from_path(self):\n        \"\"\"Setup a fake directory for testing get_module_qualname_from_path().\n\n        Create temporary directory and then create fake .py files\n        within directory structure.  We setup test cases for\n        a typical module, a path misssing a middle __init__.py,\n        no __init__.py anywhere in path, symlinking .py files.\n        \"\"\"\n\n        self.tempdir = tempfile.mkdtemp()\n        self.addCleanup(shutil.rmtree, self.tempdir)\n        self.reltempdir = os.path.relpath(self.tempdir)\n\n        # good/a/b/c/test_typical.py\n        os.makedirs(os.path.join(self.tempdir, \"good\", \"a\", \"b\", \"c\"), 0o755)\n        _touch(os.path.join(self.tempdir, \"good\", \"__init__.py\"))\n        _touch(os.path.join(self.tempdir, \"good\", \"a\", \"__init__.py\"))\n        _touch(os.path.join(self.tempdir, \"good\", \"a\", \"b\", \"__init__.py\"))\n        _touch(\n            os.path.join(self.tempdir, \"good\", \"a\", \"b\", \"c\", \"__init__.py\")\n        )\n        _touch(\n            os.path.join(\n                self.tempdir, \"good\", \"a\", \"b\", \"c\", \"test_typical.py\"\n            )\n        )\n\n        # missingmid/a/b/c/test_missingmid.py\n        os.makedirs(\n            os.path.join(self.tempdir, \"missingmid\", \"a\", \"b\", \"c\"), 0o755\n        )\n        _touch(os.path.join(self.tempdir, \"missingmid\", \"__init__.py\"))\n        # no missingmid/a/__init__.py\n        _touch(\n            os.path.join(self.tempdir, \"missingmid\", \"a\", \"b\", \"__init__.py\")\n        )\n        _touch(\n            os.path.join(\n                self.tempdir, \"missingmid\", \"a\", \"b\", \"c\", \"__init__.py\"\n            )\n        )\n        _touch(\n            os.path.join(\n                self.tempdir, \"missingmid\", \"a\", \"b\", \"c\", \"test_missingmid.py\"\n            )\n        )\n\n        # missingend/a/b/c/test_missingend.py\n        os.makedirs(\n            os.path.join(self.tempdir, \"missingend\", \"a\", \"b\", \"c\"), 0o755\n        )\n        _touch(os.path.join(self.tempdir, \"missingend\", \"__init__.py\"))\n        _touch(\n            os.path.join(self.tempdir, \"missingend\", \"a\", \"b\", \"__init__.py\")\n        )\n        # no missingend/a/b/c/__init__.py\n        _touch(\n            os.path.join(\n                self.tempdir, \"missingend\", \"a\", \"b\", \"c\", \"test_missingend.py\"\n            )\n        )\n\n        # syms/a/bsym/c/test_typical.py\n        os.makedirs(os.path.join(self.tempdir, \"syms\", \"a\"), 0o755)\n        _touch(os.path.join(self.tempdir, \"syms\", \"__init__.py\"))\n        _touch(os.path.join(self.tempdir, \"syms\", \"a\", \"__init__.py\"))\n        os.symlink(\n            os.path.join(self.tempdir, \"good\", \"a\", \"b\"),\n            os.path.join(self.tempdir, \"syms\", \"a\", \"bsym\"),\n        )\n\n    def test_get_module_qualname_from_path_abs_typical(self):\n        \"\"\"Test get_module_qualname_from_path with typical absolute paths.\"\"\"\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.tempdir, \"good\", \"a\", \"b\", \"c\", \"test_typical.py\"\n            )\n        )\n        self.assertEqual(\"good.a.b.c.test_typical\", name)\n\n    def test_get_module_qualname_from_path_with_dot(self):\n        \"\"\"Test get_module_qualname_from_path with a \".\" .\"\"\"\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\".\", \"__init__.py\")\n        )\n\n        self.assertEqual(\"__init__\", name)\n\n    def test_get_module_qualname_from_path_abs_missingmid(self):\n        # Test get_module_qualname_from_path with missing module\n        # __init__.py\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.tempdir, \"missingmid\", \"a\", \"b\", \"c\", \"test_missingmid.py\"\n            )\n        )\n        self.assertEqual(\"b.c.test_missingmid\", name)\n\n    def test_get_module_qualname_from_path_abs_missingend(self):\n        # Test get_module_qualname_from_path with no __init__.py\n        # last dir'''\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.tempdir, \"missingend\", \"a\", \"b\", \"c\", \"test_missingend.py\"\n            )\n        )\n        self.assertEqual(\"test_missingend\", name)\n\n    def test_get_module_qualname_from_path_abs_syms(self):\n        \"\"\"Test get_module_qualname_from_path with symlink in path.\"\"\"\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.tempdir, \"syms\", \"a\", \"bsym\", \"c\", \"test_typical.py\"\n            )\n        )\n        self.assertEqual(\"syms.a.bsym.c.test_typical\", name)\n\n    def test_get_module_qualname_from_path_rel_typical(self):\n        \"\"\"Test get_module_qualname_from_path with typical relative paths.\"\"\"\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.reltempdir, \"good\", \"a\", \"b\", \"c\", \"test_typical.py\"\n            )\n        )\n        self.assertEqual(\"good.a.b.c.test_typical\", name)\n\n    def test_get_module_qualname_from_path_rel_missingmid(self):\n        # Test get_module_qualname_from_path with module __init__.py\n        # missing and relative paths\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.reltempdir,\n                \"missingmid\",\n                \"a\",\n                \"b\",\n                \"c\",\n                \"test_missingmid.py\",\n            )\n        )\n        self.assertEqual(\"b.c.test_missingmid\", name)\n\n    def test_get_module_qualname_from_path_rel_missingend(self):\n        # Test get_module_qualname_from_path with __init__.py missing from\n        # last dir and using relative paths\n\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.reltempdir,\n                \"missingend\",\n                \"a\",\n                \"b\",\n                \"c\",\n                \"test_missingend.py\",\n            )\n        )\n        self.assertEqual(\"test_missingend\", name)\n\n    def test_get_module_qualname_from_path_rel_syms(self):\n        \"\"\"Test get_module_qualname_from_path with symbolic relative paths.\"\"\"\n        name = b_utils.get_module_qualname_from_path(\n            os.path.join(\n                self.reltempdir, \"syms\", \"a\", \"bsym\", \"c\", \"test_typical.py\"\n            )\n        )\n        self.assertEqual(\"syms.a.bsym.c.test_typical\", name)\n\n    def test_get_module_qualname_from_path_sys(self):\n        \"\"\"Test get_module_qualname_from_path with system module paths.\"\"\"\n\n        name = b_utils.get_module_qualname_from_path(os.__file__)\n        self.assertEqual(\"os\", name)\n\n        # This will fail because of magic for os.path. Not sure how to fix.\n        # name = b_utils.get_module_qualname_from_path(os.path.__file__)\n        # self.assertEqual(name, 'os.path')\n\n    def test_get_module_qualname_from_path_invalid_path(self):\n        \"\"\"Test get_module_qualname_from_path with invalid path.\"\"\"\n\n        name = b_utils.get_module_qualname_from_path(\"/a/b/c/d/e.py\")\n        self.assertEqual(\"e\", name)\n\n    def test_get_module_qualname_from_path_dir(self):\n        \"\"\"Test get_module_qualname_from_path with dir path.\"\"\"\n\n        self.assertRaises(\n            b_utils.InvalidModulePath,\n            b_utils.get_module_qualname_from_path,\n            \"/tmp/\",\n        )\n\n    def test_namespace_path_join(self):\n        p = b_utils.namespace_path_join(\"base1.base2\", \"name\")\n        self.assertEqual(\"base1.base2.name\", p)\n\n    def test_namespace_path_split(self):\n        (head, tail) = b_utils.namespace_path_split(\"base1.base2.name\")\n        self.assertEqual(\"base1.base2\", head)\n        self.assertEqual(\"name\", tail)\n\n    def test_get_call_name1(self):\n        \"\"\"Gets a qualified call name.\"\"\"\n        tree = ast.parse(\"a.b.c.d(x,y)\").body[0].value\n        name = b_utils.get_call_name(tree, {})\n        self.assertEqual(\"a.b.c.d\", name)\n\n    def test_get_call_name2(self):\n        \"\"\"Gets qualified call name and resolves aliases.\"\"\"\n        tree = ast.parse(\"a.b.c.d(x,y)\").body[0].value\n\n        name = b_utils.get_call_name(tree, {\"a\": \"alias.x.y\"})\n        self.assertEqual(\"alias.x.y.b.c.d\", name)\n\n        name = b_utils.get_call_name(tree, {\"a.b\": \"alias.x.y\"})\n        self.assertEqual(\"alias.x.y.c.d\", name)\n\n        name = b_utils.get_call_name(tree, {\"a.b.c.d\": \"alias.x.y\"})\n        self.assertEqual(\"alias.x.y\", name)\n\n    def test_get_call_name3(self):\n        \"\"\"Getting name for a complex call.\"\"\"\n        tree = ast.parse(\"a.list[0](x,y)\").body[0].value\n        name = b_utils._get_attr_qual_name(tree, {})\n        self.assertEqual(\"\", name)\n        # TODO(ljfisher) At best we might be able to get:\n        # self.assertEqual(name, 'a.list[0]')\n\n    def test_linerange(self):\n        with open(\"./examples/jinja2_templating.py\") as test_file:\n            tree = ast.parse(test_file.read())\n        # Check linerange returns corrent number of lines\n        line = tree.body[8]\n        lrange = b_utils.linerange(line)\n\n        # line 9 should be three lines long\n        self.assertEqual(3, len(lrange))\n\n        # the range should be the correct line numbers\n        self.assertEqual([11, 12, 13], list(lrange))\n\n    def test_path_for_function(self):\n        path = b_utils.get_path_for_function(b_utils.get_path_for_function)\n        self.assertEqual(path, b_utils.__file__)\n\n    def test_path_for_function_no_file(self):\n        self.assertIsNone(b_utils.get_path_for_function(sys.settrace))\n\n    def test_path_for_function_no_module(self):\n        self.assertIsNone(b_utils.get_path_for_function(1))\n\n    def test_escaped_representation_simple(self):\n        res = b_utils.escaped_bytes_representation(b\"ascii\")\n        self.assertEqual(res, b\"ascii\")\n\n    def test_escaped_representation_valid_not_printable(self):\n        res = b_utils.escaped_bytes_representation(b\"\\\\u0000\")\n        self.assertEqual(res, b\"\\\\x00\")\n\n    def test_escaped_representation_invalid(self):\n        res = b_utils.escaped_bytes_representation(b\"\\\\uffff\")\n        self.assertEqual(res, b\"\\\\uffff\")\n\n    def test_escaped_representation_mixed(self):\n        res = b_utils.escaped_bytes_representation(b\"ascii\\\\u0000\\\\uffff\")\n        self.assertEqual(res, b\"ascii\\\\x00\\\\uffff\")\n\n    def test_deepgetattr(self):\n        a = type(\"\", (), {})\n        a.b = type(\"\", (), {})\n        a.b.c = type(\"\", (), {})\n        a.b.c.d = \"deep value\"\n        a.b.c.d2 = \"deep value 2\"\n        a.b.c.e = \"a.b.c\"\n        self.assertEqual(\"deep value\", b_utils.deepgetattr(a.b.c, \"d\"))\n        self.assertEqual(\"deep value 2\", b_utils.deepgetattr(a.b.c, \"d2\"))\n        self.assertEqual(\"a.b.c\", b_utils.deepgetattr(a.b.c, \"e\"))\n        self.assertEqual(\"deep value\", b_utils.deepgetattr(a, \"b.c.d\"))\n        self.assertEqual(\"deep value 2\", b_utils.deepgetattr(a, \"b.c.d2\"))\n        self.assertRaises(AttributeError, b_utils.deepgetattr, a.b, \"z\")\n\n    def test_parse_ini_file(self):\n        tests = [\n            {\n                \"content\": \"[bandit]\\nexclude=/abc,/def\",\n                \"expected\": {\"exclude\": \"/abc,/def\"},\n            },\n            {\"content\": \"[Blabla]\\nsomething=something\", \"expected\": None},\n        ]\n\n        with tempfile.NamedTemporaryFile(\"r+\") as t:\n            for test in tests:\n                with open(t.name, \"w\") as f:\n                    f.write(test[\"content\"])\n\n                self.assertEqual(\n                    b_utils.parse_ini_file(t.name), test[\"expected\"]\n                )\n\n    def test_check_ast_node_good(self):\n        node = b_utils.check_ast_node(\"Call\")\n        self.assertEqual(\"Call\", node)\n\n    def test_check_ast_node_bad_node(self):\n        self.assertRaises(TypeError, b_utils.check_ast_node, \"Derp\")\n\n    def test_check_ast_node_bad_type(self):\n        self.assertRaises(TypeError, b_utils.check_ast_node, \"walk\")\n"
  },
  {
    "path": "tests/unit/formatters/__init__.py",
    "content": ""
  },
  {
    "path": "tests/unit/formatters/test_csv.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport csv\nimport tempfile\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.formatters import csv as b_csv\n\n\nclass CsvFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.context = {\n            \"filename\": self.tmp_fname,\n            \"lineno\": 4,\n            \"linerange\": [4],\n            \"col_offset\": 8,\n            \"end_col_offset\": 16,\n        }\n        self.check_name = \"hardcoded_bind_all_interfaces\"\n        self.issue = issue.Issue(\n            bandit.MEDIUM,\n            123,\n            bandit.MEDIUM,\n            \"Possible binding to all interfaces.\",\n        )\n        self.manager.out_file = self.tmp_fname\n\n        self.issue.fname = self.context[\"filename\"]\n        self.issue.lineno = self.context[\"lineno\"]\n        self.issue.linerange = self.context[\"linerange\"]\n        self.issue.col_offset = self.context[\"col_offset\"]\n        self.issue.end_col_offset = self.context[\"end_col_offset\"]\n        self.issue.test = self.check_name\n\n        self.manager.results.append(self.issue)\n\n    def test_report(self):\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_csv.report(\n                self.manager,\n                tmp_file,\n                self.issue.severity,\n                self.issue.confidence,\n            )\n\n        with open(self.tmp_fname) as f:\n            reader = csv.DictReader(f)\n            data = next(reader)\n            self.assertEqual(self.tmp_fname, data[\"filename\"])\n            self.assertEqual(self.issue.severity, data[\"issue_severity\"])\n            self.assertEqual(self.issue.confidence, data[\"issue_confidence\"])\n            self.assertEqual(self.issue.text, data[\"issue_text\"])\n            self.assertEqual(str(self.context[\"lineno\"]), data[\"line_number\"])\n            self.assertEqual(\n                str(self.context[\"linerange\"]), data[\"line_range\"]\n            )\n            self.assertEqual(self.check_name, data[\"test_name\"])\n            self.assertIsNotNone(data[\"more_info\"])\n            self.assertEqual(str(self.issue.col_offset), data[\"col_offset\"])\n            self.assertEqual(\n                str(self.issue.end_col_offset), data[\"end_col_offset\"]\n            )\n"
  },
  {
    "path": "tests/unit/formatters/test_custom.py",
    "content": "# SPDX-License-Identifier: Apache-2.0\nimport csv\nimport tempfile\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.formatters import custom\n\n\nclass CustomFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"custom\")\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.context = {\n            \"filename\": self.tmp_fname,\n            \"lineno\": 4,\n            \"linerange\": [4],\n            \"col_offset\": 30,\n            \"end_col_offset\": 38,\n        }\n        self.check_name = \"hardcoded_bind_all_interfaces\"\n        self.issue = issue.Issue(\n            bandit.MEDIUM,\n            bandit.MEDIUM,\n            text=\"Possible binding to all interfaces.\",\n        )\n        self.manager.out_file = self.tmp_fname\n\n        self.issue.fname = self.context[\"filename\"]\n        self.issue.lineno = self.context[\"lineno\"]\n        self.issue.linerange = self.context[\"linerange\"]\n        self.issue.col_offset = self.context[\"col_offset\"]\n        self.issue.end_col_offset = self.context[\"end_col_offset\"]\n        self.issue.test = self.check_name\n\n        self.manager.results.append(self.issue)\n\n    def test_report(self):\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            custom.report(\n                self.manager,\n                tmp_file,\n                self.issue.severity,\n                self.issue.confidence,\n                template=\"{line},{col},{end_col},{severity},{msg}\",\n            )\n\n        with open(self.tmp_fname) as f:\n            reader = csv.DictReader(\n                f, [\"line\", \"col\", \"end_col\", \"severity\", \"message\"]\n            )\n            data = next(reader)\n            self.assertEqual(str(self.context[\"lineno\"]), data[\"line\"])\n            self.assertEqual(str(self.context[\"col_offset\"]), data[\"col\"])\n            self.assertEqual(\n                str(self.context[\"end_col_offset\"]), data[\"end_col\"]\n            )\n            self.assertEqual(self.issue.severity, data[\"severity\"])\n            self.assertEqual(self.issue.text, data[\"message\"])\n"
  },
  {
    "path": "tests/unit/formatters/test_html.py",
    "content": "# Copyright (c) 2015 Rackspace, Inc.\n# Copyright (c) 2015 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport tempfile\nfrom unittest import mock\n\nimport bs4\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.formatters import html as b_html\n\n\nclass HtmlFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n\n        self.manager.out_file = self.tmp_fname\n\n    def test_report_with_skipped(self):\n        self.manager.skipped = [(\"abc.py\", \"File is bad\")]\n\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_html.report(self.manager, tmp_file, bandit.LOW, bandit.LOW)\n\n        with open(self.tmp_fname) as f:\n            soup = bs4.BeautifulSoup(f.read(), \"html.parser\")\n            skipped = soup.find_all(\"div\", id=\"skipped\")[0]\n\n            self.assertEqual(1, len(soup.find_all(\"div\", id=\"skipped\")))\n            self.assertIn(\"abc.py\", skipped.text)\n            self.assertIn(\"File is bad\", skipped.text)\n\n    @mock.patch(\"bandit.core.issue.Issue.get_code\")\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report_contents(self, get_issue_list, get_code):\n        self.manager.metrics.data[\"_totals\"] = {\"loc\": 1000, \"nosec\": 50}\n\n        issue_a = _get_issue_instance(severity=bandit.LOW)\n        issue_a.fname = \"abc.py\"\n        issue_a.test = \"AAAAAAA\"\n        issue_a.text = \"BBBBBBB\"\n        issue_a.confidence = \"CCCCCCC\"\n        # don't need to test severity, it determines the color which we're\n        # testing separately\n\n        issue_b = _get_issue_instance(severity=bandit.MEDIUM)\n        issue_c = _get_issue_instance(severity=bandit.HIGH)\n\n        issue_x = _get_issue_instance()\n        get_code.return_value = \"some code\"\n\n        issue_y = _get_issue_instance()\n\n        get_issue_list.return_value = collections.OrderedDict(\n            [\n                (issue_a, [issue_x, issue_y]),\n                (issue_b, [issue_x]),\n                (issue_c, [issue_y]),\n            ]\n        )\n\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_html.report(self.manager, tmp_file, bandit.LOW, bandit.LOW)\n\n        with open(self.tmp_fname) as f:\n            soup = bs4.BeautifulSoup(f.read(), \"html.parser\")\n\n            self.assertEqual(\"1000\", soup.find_all(\"span\", id=\"loc\")[0].text)\n            self.assertEqual(\"50\", soup.find_all(\"span\", id=\"nosec\")[0].text)\n\n            issue1 = soup.find_all(\"div\", id=\"issue-0\")[0]\n            issue2 = soup.find_all(\"div\", id=\"issue-1\")[0]\n            issue3 = soup.find_all(\"div\", id=\"issue-2\")[0]\n\n            # make sure the class has been applied properly\n            self.assertEqual(\n                1, len(issue1.find_all(\"div\", {\"class\": \"issue-sev-low\"}))\n            )\n\n            self.assertEqual(\n                1, len(issue2.find_all(\"div\", {\"class\": \"issue-sev-medium\"}))\n            )\n\n            self.assertEqual(\n                1, len(issue3.find_all(\"div\", {\"class\": \"issue-sev-high\"}))\n            )\n\n            # issue1 has a candidates section with 2 candidates in it\n            self.assertEqual(\n                1, len(issue1.find_all(\"div\", {\"class\": \"candidates\"}))\n            )\n            self.assertEqual(\n                2, len(issue1.find_all(\"div\", {\"class\": \"candidate\"}))\n            )\n\n            # issue2 doesn't have candidates\n            self.assertEqual(\n                0, len(issue2.find_all(\"div\", {\"class\": \"candidates\"}))\n            )\n            self.assertEqual(\n                0, len(issue2.find_all(\"div\", {\"class\": \"candidate\"}))\n            )\n\n            # issue1 doesn't have code issue 2 and 3 do\n            self.assertEqual(0, len(issue1.find_all(\"div\", {\"class\": \"code\"})))\n            self.assertEqual(1, len(issue2.find_all(\"div\", {\"class\": \"code\"})))\n            self.assertEqual(1, len(issue3.find_all(\"div\", {\"class\": \"code\"})))\n\n            # issue2 code and issue1 first candidate have code\n            element1 = issue1.find_all(\"div\", {\"class\": \"candidate\"})\n            self.assertIn(\"some code\", element1[0].text)\n            element2 = issue2.find_all(\"div\", {\"class\": \"code\"})\n            self.assertIn(\"some code\", element2[0].text)\n\n            # make sure correct things are being output in issues\n            self.assertIn(\"AAAAAAA:\", issue1.text)\n            self.assertIn(\"BBBBBBB\", issue1.text)\n            self.assertIn(\"CCCCCCC\", issue1.text)\n            self.assertIn(\"abc.py\", issue1.text)\n            self.assertIn(\"Line number: 1\", issue1.text)\n\n    @mock.patch(\"bandit.core.issue.Issue.get_code\")\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_escaping(self, get_issue_list, get_code):\n        self.manager.metrics.data[\"_totals\"] = {\"loc\": 1000, \"nosec\": 50}\n        marker = \"<tag in code>\"\n\n        issue_a = _get_issue_instance()\n        issue_x = _get_issue_instance()\n        get_code.return_value = marker\n\n        get_issue_list.return_value = {issue_a: [issue_x]}\n\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_html.report(self.manager, tmp_file, bandit.LOW, bandit.LOW)\n\n        with open(self.tmp_fname) as f:\n            contents = f.read()\n        self.assertNotIn(marker, contents)\n\n\ndef _get_issue_instance(\n    severity=bandit.MEDIUM, cwe=123, confidence=bandit.MEDIUM\n):\n    new_issue = issue.Issue(severity, cwe, confidence, \"Test issue\")\n    new_issue.fname = \"code.py\"\n    new_issue.test = \"bandit_plugin\"\n    new_issue.lineno = 1\n    return new_issue\n"
  },
  {
    "path": "tests/unit/formatters/test_json.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport json\nimport tempfile\nfrom unittest import mock\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import constants\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.core import metrics\nfrom bandit.formatters import json as b_json\n\n\nclass JsonFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.context = {\n            \"filename\": self.tmp_fname,\n            \"lineno\": 4,\n            \"linerange\": [4],\n        }\n        self.check_name = \"hardcoded_bind_all_interfaces\"\n        self.issue = issue.Issue(\n            bandit.MEDIUM,\n            issue.Cwe.MULTIPLE_BINDS,\n            bandit.MEDIUM,\n            \"Possible binding to all interfaces.\",\n        )\n\n        self.candidates = [\n            issue.Issue(\n                issue.Cwe.MULTIPLE_BINDS,\n                bandit.LOW,\n                bandit.LOW,\n                \"Candidate A\",\n                lineno=1,\n            ),\n            issue.Issue(\n                bandit.HIGH,\n                issue.Cwe.MULTIPLE_BINDS,\n                bandit.HIGH,\n                \"Candiate B\",\n                lineno=2,\n            ),\n        ]\n\n        self.manager.out_file = self.tmp_fname\n\n        self.issue.fname = self.context[\"filename\"]\n        self.issue.lineno = self.context[\"lineno\"]\n        self.issue.linerange = self.context[\"linerange\"]\n        self.issue.test = self.check_name\n\n        self.manager.results.append(self.issue)\n        self.manager.metrics = metrics.Metrics()\n\n        # mock up the metrics\n        for key in [\"_totals\", \"binding.py\"]:\n            self.manager.metrics.data[key] = {\"loc\": 4, \"nosec\": 2}\n            for criteria, default in constants.CRITERIA:\n                for rank in constants.RANKING:\n                    self.manager.metrics.data[key][f\"{criteria}.{rank}\"] = 0\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report(self, get_issue_list):\n        self.manager.files_list = [\"binding.py\"]\n        self.manager.scores = [\n            {\n                \"SEVERITY\": [0] * len(constants.RANKING),\n                \"CONFIDENCE\": [0] * len(constants.RANKING),\n            }\n        ]\n\n        get_issue_list.return_value = collections.OrderedDict(\n            [(self.issue, self.candidates)]\n        )\n\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_json.report(\n                self.manager,\n                tmp_file,\n                self.issue.severity,\n                self.issue.confidence,\n            )\n\n        with open(self.tmp_fname) as f:\n            data = json.loads(f.read())\n            self.assertIsNotNone(data[\"generated_at\"])\n            self.assertEqual(self.tmp_fname, data[\"results\"][0][\"filename\"])\n            self.assertEqual(\n                self.issue.severity, data[\"results\"][0][\"issue_severity\"]\n            )\n            self.assertEqual(\n                self.issue.confidence, data[\"results\"][0][\"issue_confidence\"]\n            )\n            self.assertEqual(self.issue.text, data[\"results\"][0][\"issue_text\"])\n            self.assertEqual(\n                self.context[\"lineno\"], data[\"results\"][0][\"line_number\"]\n            )\n            self.assertEqual(\n                self.context[\"linerange\"], data[\"results\"][0][\"line_range\"]\n            )\n            self.assertEqual(self.check_name, data[\"results\"][0][\"test_name\"])\n            self.assertIn(\"candidates\", data[\"results\"][0])\n            self.assertIn(\"more_info\", data[\"results\"][0])\n            self.assertIsNotNone(data[\"results\"][0][\"more_info\"])\n"
  },
  {
    "path": "tests/unit/formatters/test_sarif.py",
    "content": "# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport json\nimport tempfile\nfrom unittest import mock\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import constants\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.core import metrics\nfrom bandit.formatters import sarif\n\n\nclass SarifFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.context = {\n            \"filename\": self.tmp_fname,\n            \"lineno\": 4,\n            \"linerange\": [4],\n            \"code\": (\n                \"import socket\\n\\n\"\n                \"s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\\n\"\n                \"s.bind(('0.0.0.0', 31137))\"\n            ),\n        }\n        self.check_name = \"hardcoded_bind_all_interfaces\"\n        self.issue = issue.Issue(\n            severity=bandit.MEDIUM,\n            cwe=issue.Cwe.MULTIPLE_BINDS,\n            confidence=bandit.MEDIUM,\n            text=\"Possible binding to all interfaces.\",\n            test_id=\"B104\",\n        )\n\n        self.candidates = [\n            issue.Issue(\n                issue.Cwe.MULTIPLE_BINDS,\n                bandit.LOW,\n                bandit.LOW,\n                \"Candidate A\",\n                lineno=1,\n            ),\n            issue.Issue(\n                bandit.HIGH,\n                issue.Cwe.MULTIPLE_BINDS,\n                bandit.HIGH,\n                \"Candiate B\",\n                lineno=2,\n            ),\n        ]\n\n        self.manager.out_file = self.tmp_fname\n\n        self.issue.fname = self.context[\"filename\"]\n        self.issue.lineno = self.context[\"lineno\"]\n        self.issue.linerange = self.context[\"linerange\"]\n        self.issue.code = self.context[\"code\"]\n        self.issue.test = self.check_name\n\n        self.manager.results.append(self.issue)\n        self.manager.metrics = metrics.Metrics()\n\n        # mock up the metrics\n        for key in [\"_totals\", \"binding.py\"]:\n            self.manager.metrics.data[key] = {\"loc\": 4, \"nosec\": 2}\n            for criteria, default in constants.CRITERIA:\n                for rank in constants.RANKING:\n                    self.manager.metrics.data[key][f\"{criteria}.{rank}\"] = 0\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report(self, get_issue_list):\n        self.manager.files_list = [\"binding.py\"]\n        self.manager.scores = [\n            {\n                \"SEVERITY\": [0] * len(constants.RANKING),\n                \"CONFIDENCE\": [0] * len(constants.RANKING),\n            }\n        ]\n\n        get_issue_list.return_value = collections.OrderedDict(\n            [(self.issue, self.candidates)]\n        )\n\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            sarif.report(\n                self.manager,\n                tmp_file,\n                self.issue.severity,\n                self.issue.confidence,\n            )\n\n        with open(self.tmp_fname) as f:\n            data = json.loads(f.read())\n            run = data[\"runs\"][0]\n            self.assertEqual(sarif.SCHEMA_URI, data[\"$schema\"])\n            self.assertEqual(sarif.SCHEMA_VER, data[\"version\"])\n            driver = run[\"tool\"][\"driver\"]\n            self.assertEqual(\"Bandit\", driver[\"name\"])\n            self.assertEqual(bandit.__author__, driver[\"organization\"])\n            self.assertEqual(bandit.__version__, driver[\"semanticVersion\"])\n            self.assertEqual(\"B104\", driver[\"rules\"][0][\"id\"])\n            self.assertEqual(self.check_name, driver[\"rules\"][0][\"name\"])\n            self.assertIn(\"security\", driver[\"rules\"][0][\"properties\"][\"tags\"])\n            self.assertIn(\n                \"external/cwe/cwe-605\",\n                driver[\"rules\"][0][\"properties\"][\"tags\"],\n            )\n            self.assertEqual(\n                \"medium\", driver[\"rules\"][0][\"properties\"][\"precision\"]\n            )\n            invocation = run[\"invocations\"][0]\n            self.assertTrue(invocation[\"executionSuccessful\"])\n            self.assertIsNotNone(invocation[\"endTimeUtc\"])\n            result = run[\"results\"][0]\n            # If the level is \"warning\" like in this case, SARIF will remove\n            # from output, as \"warning\" is the default value.\n            self.assertIsNone(result.get(\"level\"))\n            self.assertEqual(self.issue.text, result[\"message\"][\"text\"])\n            physicalLocation = result[\"locations\"][0][\"physicalLocation\"]\n            self.assertEqual(\n                self.context[\"linerange\"][0],\n                physicalLocation[\"region\"][\"startLine\"],\n            )\n            self.assertEqual(\n                self.context[\"linerange\"][0],\n                physicalLocation[\"region\"][\"endLine\"],\n            )\n            self.assertIn(\n                self.tmp_fname,\n                physicalLocation[\"artifactLocation\"][\"uri\"],\n            )\n"
  },
  {
    "path": "tests/unit/formatters/test_screen.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n# Copyright (c) 2015 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport tempfile\nfrom unittest import mock\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import docs_utils\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.formatters import screen\n\n\nclass ScreenFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n\n    @mock.patch(\"bandit.core.issue.Issue.get_code\")\n    def test_output_issue(self, get_code):\n        issue = _get_issue_instance()\n        get_code.return_value = \"DDDDDDD\"\n        indent_val = \"CCCCCCC\"\n\n        def _template(_issue, _indent_val, _code, _color):\n            return_val = [\n                \"{}{}>> Issue: [{}:{}] {}\".format(\n                    _indent_val,\n                    _color,\n                    _issue.test_id,\n                    _issue.test,\n                    _issue.text,\n                ),\n                \"{}   Severity: {}   Confidence: {}\".format(\n                    _indent_val,\n                    _issue.severity.capitalize(),\n                    _issue.confidence.capitalize(),\n                ),\n                f\"{_indent_val}   CWE: {_issue.cwe}\",\n                f\"{_indent_val}   More Info: \"\n                f\"{docs_utils.get_url(_issue.test_id)}\",\n                \"{}   Location: {}:{}:{}{}\".format(\n                    _indent_val,\n                    _issue.fname,\n                    _issue.lineno,\n                    _issue.col_offset,\n                    screen.COLOR[\"DEFAULT\"],\n                ),\n            ]\n            if _code:\n                return_val.append(f\"{_indent_val}{_code}\")\n            return \"\\n\".join(return_val)\n\n        issue_text = screen._output_issue_str(issue, indent_val)\n        expected_return = _template(\n            issue, indent_val, \"DDDDDDD\", screen.COLOR[\"MEDIUM\"]\n        )\n        self.assertEqual(expected_return, issue_text)\n\n        issue_text = screen._output_issue_str(\n            issue, indent_val, show_code=False\n        )\n        expected_return = _template(\n            issue, indent_val, \"\", screen.COLOR[\"MEDIUM\"]\n        )\n        self.assertEqual(expected_return, issue_text)\n\n        issue.lineno = \"\"\n        issue.col_offset = \"\"\n        issue_text = screen._output_issue_str(\n            issue, indent_val, show_lineno=False\n        )\n        expected_return = _template(\n            issue, indent_val, \"DDDDDDD\", screen.COLOR[\"MEDIUM\"]\n        )\n        self.assertEqual(expected_return, issue_text)\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_no_issues(self, get_issue_list):\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.manager.out_file = self.tmp_fname\n\n        get_issue_list.return_value = collections.OrderedDict()\n        with mock.patch(\"bandit.formatters.screen.do_print\") as m:\n            with open(self.tmp_fname, \"w\") as tmp_file:\n                screen.report(\n                    self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n                )\n            self.assertIn(\n                \"No issues identified.\",\n                \"\\n\".join([str(a) for a in m.call_args]),\n            )\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report_nobaseline(self, get_issue_list):\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.manager.out_file = self.tmp_fname\n\n        self.manager.verbose = True\n        self.manager.files_list = [\"binding.py\"]\n\n        self.manager.scores = [\n            {\"SEVERITY\": [0, 0, 0, 1], \"CONFIDENCE\": [0, 0, 0, 1]}\n        ]\n\n        self.manager.skipped = [(\"abc.py\", \"File is bad\")]\n        self.manager.excluded_files = [\"def.py\"]\n\n        issue_a = _get_issue_instance()\n        issue_b = _get_issue_instance()\n\n        get_issue_list.return_value = [issue_a, issue_b]\n\n        self.manager.metrics.data[\"_totals\"] = {\"loc\": 1000, \"nosec\": 50}\n        for category in [\"SEVERITY\", \"CONFIDENCE\"]:\n            for level in [\"UNDEFINED\", \"LOW\", \"MEDIUM\", \"HIGH\"]:\n                self.manager.metrics.data[\"_totals\"][f\"{category}.{level}\"] = 1\n\n        # Validate that we're outputting the correct issues\n        output_str_fn = \"bandit.formatters.screen._output_issue_str\"\n        with mock.patch(output_str_fn) as output_str:\n            output_str.return_value = \"ISSUE_OUTPUT_TEXT\"\n\n            with open(self.tmp_fname, \"w\") as tmp_file:\n                screen.report(\n                    self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n                )\n\n            calls = [\n                mock.call(issue_a, \"\", lines=5),\n                mock.call(issue_b, \"\", lines=5),\n            ]\n\n            output_str.assert_has_calls(calls, any_order=True)\n\n        # Validate that we're outputting all of the expected fields and the\n        # correct values\n        with mock.patch(\"bandit.formatters.screen.do_print\") as m:\n            with open(self.tmp_fname, \"w\") as tmp_file:\n                screen.report(\n                    self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n                )\n\n            data = \"\\n\".join([str(a) for a in m.call_args[0][0]])\n\n            expected = \"Run started\"\n            self.assertIn(expected, data)\n\n            expected_items = [\n                screen.header(\"Files in scope (1):\"),\n                \"\\n\\tbinding.py (score: {SEVERITY: 1, CONFIDENCE: 1})\",\n            ]\n\n            for item in expected_items:\n                self.assertIn(item, data)\n\n            expected = screen.header(\"Files excluded (1):\") + \"\\n\\tdef.py\"\n            self.assertIn(expected, data)\n\n            expected = (\n                \"Total lines of code: 1000\\n\\tTotal lines skipped \"\n                \"(#nosec): 50\"\n            )\n            self.assertIn(expected, data)\n\n            expected = (\n                \"Total issues (by severity):\\n\\t\\tUndefined: 1\\n\\t\\t\"\n                \"Low: 1\\n\\t\\tMedium: 1\\n\\t\\tHigh: 1\"\n            )\n            self.assertIn(expected, data)\n\n            expected = (\n                \"Total issues (by confidence):\\n\\t\\tUndefined: 1\\n\\t\\t\"\n                \"Low: 1\\n\\t\\tMedium: 1\\n\\t\\tHigh: 1\"\n            )\n            self.assertIn(expected, data)\n\n            expected = (\n                screen.header(\"Files skipped (1):\")\n                + \"\\n\\tabc.py (File is bad)\"\n            )\n            self.assertIn(expected, data)\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report_baseline(self, get_issue_list):\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.manager.out_file = self.tmp_fname\n\n        issue_a = _get_issue_instance()\n        issue_b = _get_issue_instance()\n\n        issue_x = _get_issue_instance()\n        issue_x.fname = \"x\"\n        issue_y = _get_issue_instance()\n        issue_y.fname = \"y\"\n        issue_z = _get_issue_instance()\n        issue_z.fname = \"z\"\n\n        get_issue_list.return_value = collections.OrderedDict(\n            [(issue_a, [issue_x]), (issue_b, [issue_y, issue_z])]\n        )\n\n        # Validate that we're outputting the correct issues\n        indent_val = \" \" * 10\n        output_str_fn = \"bandit.formatters.screen._output_issue_str\"\n        with mock.patch(output_str_fn) as output_str:\n            output_str.return_value = \"ISSUE_OUTPUT_TEXT\"\n\n            with open(self.tmp_fname, \"w\") as tmp_file:\n                screen.report(\n                    self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n                )\n\n            calls = [\n                mock.call(issue_a, \"\", lines=5),\n                mock.call(issue_b, \"\", show_code=False, show_lineno=False),\n                mock.call(issue_y, indent_val, lines=5),\n                mock.call(issue_z, indent_val, lines=5),\n            ]\n\n            output_str.assert_has_calls(calls, any_order=True)\n\n\ndef _get_issue_instance(\n    severity=bandit.MEDIUM, cwe=123, confidence=bandit.MEDIUM\n):\n    new_issue = issue.Issue(severity, cwe, confidence, \"Test issue\")\n    new_issue.fname = \"code.py\"\n    new_issue.test = \"bandit_plugin\"\n    new_issue.lineno = 1\n    return new_issue\n"
  },
  {
    "path": "tests/unit/formatters/test_text.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n# Copyright (c) 2015 Hewlett Packard Enterprise\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport tempfile\nfrom unittest import mock\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import docs_utils\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.formatters import text as b_text\n\n\nclass TextFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n\n    @mock.patch(\"bandit.core.issue.Issue.get_code\")\n    def test_output_issue(self, get_code):\n        issue = _get_issue_instance()\n        get_code.return_value = \"DDDDDDD\"\n        indent_val = \"CCCCCCC\"\n\n        def _template(_issue, _indent_val, _code):\n            return_val = [\n                \"{}>> Issue: [{}:{}] {}\".format(\n                    _indent_val, _issue.test_id, _issue.test, _issue.text\n                ),\n                \"{}   Severity: {}   Confidence: {}\".format(\n                    _indent_val,\n                    _issue.severity.capitalize(),\n                    _issue.confidence.capitalize(),\n                ),\n                f\"{_indent_val}   CWE: {_issue.cwe}\",\n                f\"{_indent_val}   More Info: \"\n                f\"{docs_utils.get_url(_issue.test_id)}\",\n                \"{}   Location: {}:{}:{}\".format(\n                    _indent_val, _issue.fname, _issue.lineno, _issue.col_offset\n                ),\n            ]\n            if _code:\n                return_val.append(f\"{_indent_val}{_code}\")\n            return \"\\n\".join(return_val)\n\n        issue_text = b_text._output_issue_str(issue, indent_val)\n        expected_return = _template(issue, indent_val, \"DDDDDDD\")\n        self.assertEqual(expected_return, issue_text)\n\n        issue_text = b_text._output_issue_str(\n            issue, indent_val, show_code=False\n        )\n        expected_return = _template(issue, indent_val, \"\")\n        self.assertEqual(expected_return, issue_text)\n\n        issue.lineno = \"\"\n        issue.col_offset = \"\"\n        issue_text = b_text._output_issue_str(\n            issue, indent_val, show_lineno=False\n        )\n        expected_return = _template(issue, indent_val, \"DDDDDDD\")\n        self.assertEqual(expected_return, issue_text)\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_no_issues(self, get_issue_list):\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.manager.out_file = self.tmp_fname\n\n        get_issue_list.return_value = collections.OrderedDict()\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_text.report(\n                self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n            )\n\n        with open(self.tmp_fname) as f:\n            data = f.read()\n            self.assertIn(\"No issues identified.\", data)\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report_nobaseline(self, get_issue_list):\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.manager.out_file = self.tmp_fname\n\n        self.manager.verbose = True\n        self.manager.files_list = [\"binding.py\"]\n\n        self.manager.scores = [\n            {\"SEVERITY\": [0, 0, 0, 1], \"CONFIDENCE\": [0, 0, 0, 1]}\n        ]\n\n        self.manager.skipped = [(\"abc.py\", \"File is bad\")]\n        self.manager.excluded_files = [\"def.py\"]\n\n        issue_a = _get_issue_instance()\n        issue_b = _get_issue_instance()\n\n        get_issue_list.return_value = [issue_a, issue_b]\n\n        self.manager.metrics.data[\"_totals\"] = {\n            \"loc\": 1000,\n            \"nosec\": 50,\n            \"skipped_tests\": 0,\n        }\n        for category in [\"SEVERITY\", \"CONFIDENCE\"]:\n            for level in [\"UNDEFINED\", \"LOW\", \"MEDIUM\", \"HIGH\"]:\n                self.manager.metrics.data[\"_totals\"][f\"{category}.{level}\"] = 1\n\n        # Validate that we're outputting the correct issues\n        output_str_fn = \"bandit.formatters.text._output_issue_str\"\n        with mock.patch(output_str_fn) as output_str:\n            output_str.return_value = \"ISSUE_OUTPUT_TEXT\"\n\n            with open(self.tmp_fname, \"w\") as tmp_file:\n                b_text.report(\n                    self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n                )\n\n            calls = [\n                mock.call(issue_a, \"\", lines=5),\n                mock.call(issue_b, \"\", lines=5),\n            ]\n\n            output_str.assert_has_calls(calls, any_order=True)\n\n        # Validate that we're outputting all of the expected fields and the\n        # correct values\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_text.report(\n                self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n            )\n        with open(self.tmp_fname) as f:\n            data = f.read()\n\n            expected_items = [\n                \"Run started\",\n                \"Files in scope (1)\",\n                \"binding.py (score: \",\n                \"CONFIDENCE: 1\",\n                \"SEVERITY: 1\",\n                f\"CWE: {str(issue.Cwe(issue.Cwe.MULTIPLE_BINDS))}\",\n                \"Files excluded (1):\",\n                \"def.py\",\n                \"Undefined: 1\",\n                \"Low: 1\",\n                \"Medium: 1\",\n                \"High: 1\",\n                \"Total lines skipped \",\n                \"(#nosec): 50\",\n                \"Total potential issues skipped due to specifically being \",\n                \"disabled (e.g., #nosec BXXX): 0\",\n                \"Total issues (by severity)\",\n                \"Total issues (by confidence)\",\n                \"Files skipped (1)\",\n                \"abc.py (File is bad)\",\n            ]\n            for item in expected_items:\n                self.assertIn(item, data)\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report_baseline(self, get_issue_list):\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.manager.out_file = self.tmp_fname\n\n        issue_a = _get_issue_instance()\n        issue_b = _get_issue_instance()\n\n        issue_x = _get_issue_instance()\n        issue_x.fname = \"x\"\n        issue_y = _get_issue_instance()\n        issue_y.fname = \"y\"\n        issue_z = _get_issue_instance()\n        issue_z.fname = \"z\"\n\n        get_issue_list.return_value = collections.OrderedDict(\n            [(issue_a, [issue_x]), (issue_b, [issue_y, issue_z])]\n        )\n\n        # Validate that we're outputting the correct issues\n        indent_val = \" \" * 10\n        output_str_fn = \"bandit.formatters.text._output_issue_str\"\n        with mock.patch(output_str_fn) as output_str:\n            output_str.return_value = \"ISSUE_OUTPUT_TEXT\"\n\n            with open(self.tmp_fname, \"w\") as tmp_file:\n                b_text.report(\n                    self.manager, tmp_file, bandit.LOW, bandit.LOW, lines=5\n                )\n\n            calls = [\n                mock.call(issue_a, \"\", lines=5),\n                mock.call(issue_b, \"\", show_code=False, show_lineno=False),\n                mock.call(issue_y, indent_val, lines=5),\n                mock.call(issue_z, indent_val, lines=5),\n            ]\n\n            output_str.assert_has_calls(calls, any_order=True)\n\n\ndef _get_issue_instance(\n    severity=bandit.MEDIUM,\n    cwe=issue.Cwe.MULTIPLE_BINDS,\n    confidence=bandit.MEDIUM,\n):\n    new_issue = issue.Issue(severity, cwe, confidence, \"Test issue\")\n    new_issue.fname = \"code.py\"\n    new_issue.test = \"bandit_plugin\"\n    new_issue.lineno = 1\n    return new_issue\n"
  },
  {
    "path": "tests/unit/formatters/test_xml.py",
    "content": "# Copyright (c) 2015 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport tempfile\nfrom xml.etree import ElementTree as ET\n\nimport testtools\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.formatters import xml as b_xml\n\n\nclass XmlFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.context = {\n            \"filename\": self.tmp_fname,\n            \"lineno\": 4,\n            \"linerange\": [4],\n        }\n        self.check_name = \"hardcoded_bind_all_interfaces\"\n        self.issue = issue.Issue(\n            bandit.MEDIUM,\n            issue.Cwe.MULTIPLE_BINDS,\n            bandit.MEDIUM,\n            \"Possible binding to all interfaces.\",\n        )\n        self.manager.out_file = self.tmp_fname\n\n        self.issue.fname = self.context[\"filename\"]\n        self.issue.lineno = self.context[\"lineno\"]\n        self.issue.linerange = self.context[\"linerange\"]\n        self.issue.test = self.check_name\n\n        self.manager.results.append(self.issue)\n\n    def _xml_to_dict(self, t):\n        d = {t.tag: {} if t.attrib else None}\n        children = list(t)\n        if children:\n            dd = collections.defaultdict(list)\n            for dc in map(self._xml_to_dict, children):\n                for k, v in dc.items():\n                    dd[k].append(v)\n            d = {t.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}\n        if t.attrib:\n            d[t.tag].update((\"@\" + k, v) for k, v in t.attrib.items())\n        if t.text:\n            text = t.text.strip()\n            if children or t.attrib:\n                if text:\n                    d[t.tag][\"#text\"] = text\n            else:\n                d[t.tag] = text\n        return d\n\n    def test_report(self):\n        with open(self.tmp_fname, \"wb\") as tmp_file:\n            b_xml.report(\n                self.manager,\n                tmp_file,\n                self.issue.severity,\n                self.issue.confidence,\n            )\n\n        with open(self.tmp_fname) as f:\n            data = self._xml_to_dict(ET.XML(f.read()))\n            self.assertEqual(\n                self.tmp_fname, data[\"testsuite\"][\"testcase\"][\"@classname\"]\n            )\n            self.assertEqual(\n                self.issue.text,\n                data[\"testsuite\"][\"testcase\"][\"error\"][\"@message\"],\n            )\n            self.assertEqual(\n                self.check_name, data[\"testsuite\"][\"testcase\"][\"@name\"]\n            )\n            self.assertIsNotNone(\n                data[\"testsuite\"][\"testcase\"][\"error\"][\"@more_info\"]\n            )\n"
  },
  {
    "path": "tests/unit/formatters/test_yaml.py",
    "content": "# Copyright (c) 2017 VMware, Inc.\n#\n# SPDX-License-Identifier: Apache-2.0\nimport collections\nimport tempfile\nfrom unittest import mock\n\nimport testtools\nimport yaml\n\nimport bandit\nfrom bandit.core import config\nfrom bandit.core import constants\nfrom bandit.core import issue\nfrom bandit.core import manager\nfrom bandit.core import metrics\nfrom bandit.formatters import json as b_json\n\n\nclass YamlFormatterTests(testtools.TestCase):\n    def setUp(self):\n        super().setUp()\n        conf = config.BanditConfig()\n        self.manager = manager.BanditManager(conf, \"file\")\n        (tmp_fd, self.tmp_fname) = tempfile.mkstemp()\n        self.context = {\n            \"filename\": self.tmp_fname,\n            \"lineno\": 4,\n            \"linerange\": [4],\n        }\n        self.check_name = \"hardcoded_bind_all_interfaces\"\n        self.issue = issue.Issue(\n            bandit.MEDIUM,\n            123,\n            bandit.MEDIUM,\n            \"Possible binding to all interfaces.\",\n        )\n\n        self.candidates = [\n            issue.Issue(bandit.LOW, 123, bandit.LOW, \"Candidate A\", lineno=1),\n            issue.Issue(bandit.HIGH, 123, bandit.HIGH, \"Candiate B\", lineno=2),\n        ]\n\n        self.manager.out_file = self.tmp_fname\n\n        self.issue.fname = self.context[\"filename\"]\n        self.issue.lineno = self.context[\"lineno\"]\n        self.issue.linerange = self.context[\"linerange\"]\n        self.issue.test = self.check_name\n\n        self.manager.results.append(self.issue)\n        self.manager.metrics = metrics.Metrics()\n\n        # mock up the metrics\n        for key in [\"_totals\", \"binding.py\"]:\n            self.manager.metrics.data[key] = {\"loc\": 4, \"nosec\": 2}\n            for criteria, default in constants.CRITERIA:\n                for rank in constants.RANKING:\n                    self.manager.metrics.data[key][f\"{criteria}.{rank}\"] = 0\n\n    @mock.patch(\"bandit.core.manager.BanditManager.get_issue_list\")\n    def test_report(self, get_issue_list):\n        self.manager.files_list = [\"binding.py\"]\n        self.manager.scores = [\n            {\n                \"SEVERITY\": [0] * len(constants.RANKING),\n                \"CONFIDENCE\": [0] * len(constants.RANKING),\n            }\n        ]\n\n        get_issue_list.return_value = collections.OrderedDict(\n            [(self.issue, self.candidates)]\n        )\n\n        with open(self.tmp_fname, \"w\") as tmp_file:\n            b_json.report(\n                self.manager,\n                tmp_file,\n                self.issue.severity,\n                self.issue.confidence,\n            )\n\n        with open(self.tmp_fname) as f:\n            data = yaml.load(f.read(), Loader=yaml.SafeLoader)\n            self.assertIsNotNone(data[\"generated_at\"])\n            self.assertEqual(self.tmp_fname, data[\"results\"][0][\"filename\"])\n            self.assertEqual(\n                self.issue.severity, data[\"results\"][0][\"issue_severity\"]\n            )\n            self.assertEqual(\n                self.issue.confidence, data[\"results\"][0][\"issue_confidence\"]\n            )\n            self.assertEqual(self.issue.text, data[\"results\"][0][\"issue_text\"])\n            self.assertEqual(\n                self.context[\"lineno\"], data[\"results\"][0][\"line_number\"]\n            )\n            self.assertEqual(\n                self.context[\"linerange\"], data[\"results\"][0][\"line_range\"]\n            )\n            self.assertEqual(self.check_name, data[\"results\"][0][\"test_name\"])\n            self.assertIn(\"candidates\", data[\"results\"][0])\n            self.assertIn(\"more_info\", data[\"results\"][0])\n            self.assertIsNotNone(data[\"results\"][0][\"more_info\"])\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nminversion = 3.2.0\nenvlist = py310,pep8\n\n[testenv]\nusedevelop = True\ninstall_command = pip install {opts} {packages}\nsetenv =\n    VIRTUAL_ENV={envdir}\ndeps =\n       -r{toxinidir}/requirements.txt\n       -r{toxinidir}/test-requirements.txt\nextras =\n    yaml\n    toml\n    baseline\n    sarif\ncommands =\n    find bandit -type f -name \"*.pyc\" -delete\n    stestr run {posargs}\nallowlist_externals =\n    find\npassenv =\n    http_proxy\n    HTTP_PROXY\n    https_proxy\n    HTTPS_PROXY\n    no_proxy\n    NO_PROXY\n\n[testenv:linters]\ndeps = {[testenv:pep8]deps}\nusedevelop = False\ncommands = flake8 {posargs} bandit\n           flake8 {posargs} tests\n           bandit-baseline -r bandit -ll -ii\n\n[testenv:pep8]\nignore_errors = true\ndeps = {[testenv]deps}\n       .\nusedevelop = False\ncommands = flake8 {posargs} bandit\n           flake8 {posargs} tests\n           -{[testenv:pylint]commands}\n           bandit-baseline -r bandit -ll -ii\n\n[testenv:venv]\ncommands = {posargs}\n\n[testenv:codesec]\ndeps = {[testenv]deps}\n       .\nusedevelop = False\ncommands = bandit-baseline -r bandit -ll -ii\n\n[testenv:cover]\nsetenv =\n    {[testenv]setenv}\n    PYTHON=coverage run --source bandit --parallel-mode\ncommands =\n    coverage erase\n    stestr run '{posargs}'\n    coverage report\n\n[testenv:docs]\ndeps = -r{toxinidir}/doc/requirements.txt\ncommands=\n    sphinx-build doc/source doc/build\n\n[testenv:manpage]\ndeps = -r{toxinidir}/doc/requirements.txt\ncommands=\n    sphinx-build -b man doc/source doc/build/man\n\n[flake8]\n# [H106] Don't put vim configuration in source files.\n# [H203] Use assertIs(Not)None to check for None.\nshow-source = True\nexclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build\nenable-extensions = H106,H203\n\n[testenv:pylint]\ncommands = -pylint --rcfile=pylintrc bandit\n\n[testenv:format]\nskip_install = true\ndeps =\n    pre-commit\ncommands =\n    pre-commit run --all-files --show-diff-on-failure\n"
  }
]