# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pretend
import pytest
import yara

from warehouse.malware.checks.setup_patterns import check as c
from warehouse.malware.models import (
    MalwareCheckState,
    VerdictClassification,
    VerdictConfidence,
)

from .....common.db.malware import MalwareCheckFactory
from .....common.db.packaging import FileFactory


def test_initializes(db_session):
    check_model = MalwareCheckFactory.create(
        name="SetupPatternCheck", state=MalwareCheckState.Enabled
    )
    check = c.SetupPatternCheck(db_session)

    assert check.id == check_model.id
    assert isinstance(check._yara_rules, yara.Rules)


@pytest.mark.parametrize(
    ("obj", "file_url"), [(None, pretend.stub()), (pretend.stub(), None)]
)
def test_scan_missing_kwargs(db_session, obj, file_url):
    MalwareCheckFactory.create(
        name="SetupPatternCheck", state=MalwareCheckState.Enabled
    )
    check = c.SetupPatternCheck(db_session)
    with pytest.raises(c.FatalCheckException):
        check.scan(obj=obj, file_url=file_url)


def test_scan_non_sdist(db_session):
    MalwareCheckFactory.create(
        name="SetupPatternCheck", state=MalwareCheckState.Enabled
    )
    check = c.SetupPatternCheck(db_session)

    file = FileFactory.create(packagetype="bdist_wheel")

    check.scan(obj=file, file_url=pretend.stub())

    assert check._verdicts == []


def test_scan_no_setup_contents(db_session, monkeypatch):
    monkeypatch.setattr(
        c, "fetch_url_content", pretend.call_recorder(lambda *a: pretend.stub())
    )
    monkeypatch.setattr(
        c, "extract_file_content", pretend.call_recorder(lambda *a: None)
    )

    MalwareCheckFactory.create(
        name="SetupPatternCheck", state=MalwareCheckState.Enabled
    )
    check = c.SetupPatternCheck(db_session)

    file = FileFactory.create(packagetype="sdist")

    check.scan(obj=file, file_url=pretend.stub())

    assert len(check._verdicts) == 1
    assert check._verdicts[0].check_id == check.id
    assert check._verdicts[0].file_id == file.id
    assert check._verdicts[0].classification == VerdictClassification.Indeterminate
    assert check._verdicts[0].confidence == VerdictConfidence.High
    assert (
        check._verdicts[0].message
        == "sdist does not contain a suitable setup.py for analysis"
    )


@pytest.mark.parametrize("benign", ["", """from os import path"""])
def test_scan_benign_contents(db_session, monkeypatch, benign):
    monkeypatch.setattr(
        c, "fetch_url_content", pretend.call_recorder(lambda *a: pretend.stub())
    )
    monkeypatch.setattr(
        c,
        "extract_file_content",
        pretend.call_recorder(
            lambda *a: b"this is a benign string\n" + benign.encode("utf-8")
        ),
    )

    MalwareCheckFactory.create(
        name="SetupPatternCheck", state=MalwareCheckState.Enabled
    )
    check = c.SetupPatternCheck(db_session)

    file = FileFactory.create(packagetype="sdist")

    check.scan(obj=file, file_url=pretend.stub())

    assert len(check._verdicts) == 1
    assert check._verdicts[0].check_id == check.id
    assert check._verdicts[0].file_id == file.id
    assert check._verdicts[0].classification == VerdictClassification.Benign
    assert check._verdicts[0].confidence == VerdictConfidence.Low
    assert check._verdicts[0].message == "No malicious patterns found in setup.py"


@pytest.mark.parametrize(
    "malicious, rule",
    [
        # process_spawn_in_setup
        ("""os.system('cat /etc/passwd')""", "process_spawn_in_setup"),
        ("""os.popen('cat /etc/passwd')""", "process_spawn_in_setup"),
        ("""os.popen3('cat /etc/passwd')""", "process_spawn_in_setup"),
        ("""os.spawn('cat /etc/passwd')""", "process_spawn_in_setup"),
        ("""os.spawnve('cat /etc/passwd')""", "process_spawn_in_setup"),
        ("""os.posix_spawn('cat /etc/passwd')""", "process_spawn_in_setup"),
        ("""os.posix_spawnp('cat /etc/passwd')""", "process_spawn_in_setup"),
        (
            """os.exec('malicious_code')""",
            "process_spawn_in_setup:metaprogramming_in_setup",
        ),
        ("""os.execve('malicious_code')""", "process_spawn_in_setup"),
        (
            """
            from os import *
            system('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import path, system
            system('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import system
            system('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import popen
            popen('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import popen3
            popen3('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import spawn
            spawn('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import spawnve
            spawnve('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import posix_spawn
            posix_spawn('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import posix_spawnp
            posix_spawnp('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            from os import exec
            exec('cat /etc/passwd')
            """,
            "process_spawn_in_setup:metaprogramming_in_setup",
        ),
        (
            """
            from os import execve
            execve('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        # subprocess_in_setup
        ("subprocess.run('...')", "subprocess_in_setup"),
        ("subprocess.Popen('...')", "subprocess_in_setup"),
        ("subprocess.call('...')", "subprocess_in_setup"),
        ("subprocess.check_call('...')", "subprocess_in_setup"),
        ("subprocess.check_output('...')", "subprocess_in_setup"),
        (
            """
            from subprocess import run
            run('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            from subprocess import Popen
            Popen('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            from subprocess import call
            call('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            from subprocess import check_call
            check_call('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            from subprocess import check_output
            check_output('...')
            """,
            "subprocess_in_setup",
        ),
        # networking_in_setup
        ("from socket import something", "networking_in_setup"),
        ("from socket.something import something", "networking_in_setup"),
        ("import socket", "networking_in_setup"),
        ("from socketserver import something", "networking_in_setup"),
        ("from socketserver.something import something", "networking_in_setup"),
        ("import socketserver", "networking_in_setup"),
        ("from ssl import something", "networking_in_setup"),
        ("from ssl.something import something", "networking_in_setup"),
        ("import ssl", "networking_in_setup"),
        ("from ftplib import something", "networking_in_setup"),
        ("from http.something import something", "networking_in_setup"),
        ("import http", "networking_in_setup"),
        ("import http", "networking_in_setup"),
        ("from urllib import something", "networking_in_setup"),
        ("from urllib.something import something", "networking_in_setup"),
        ("import urllib", "networking_in_setup"),
        ("from xmlrpc import something", "networking_in_setup"),
        ("from xmlrpc.something import something", "networking_in_setup"),
        ("import xmlrpc", "networking_in_setup"),
        # deserialization_in_setup
        ("from pickle import something", "deserialization_in_setup"),
        ("from pickle.something import something", "deserialization_in_setup"),
        ("import pickle", "deserialization_in_setup"),
        ("from base64 import something", "deserialization_in_setup"),
        ("from base64.something import something", "deserialization_in_setup"),
        ("import base64", "deserialization_in_setup"),
        ("from binhex import something", "deserialization_in_setup"),
        ("from binhex.something import something", "deserialization_in_setup"),
        ("import binhex", "deserialization_in_setup"),
        # metaprogramming_in_setup
        ("from inspect import something", "metaprogramming_in_setup"),
        ("from inspect.something import something", "metaprogramming_in_setup"),
        ("import inspect", "metaprogramming_in_setup"),
        ("from compileall import something", "metaprogramming_in_setup"),
        ("from compileall.something import something", "metaprogramming_in_setup"),
        ("import compileall", "metaprogramming_in_setup"),
        ("from py_compile import something", "metaprogramming_in_setup"),
        ("from py_compile.something import something", "metaprogramming_in_setup"),
        ("import py_compile", "metaprogramming_in_setup"),
        ("from builtins import something", "metaprogramming_in_setup"),
        ("from builtins.something import something", "metaprogramming_in_setup"),
        ("import builtins", "metaprogramming_in_setup"),
        ("__builtins__.bla", "metaprogramming_in_setup"),
        ("from importlib import something", "metaprogramming_in_setup"),
        ("from importlib.something import something", "metaprogramming_in_setup"),
        ("import importlib", "metaprogramming_in_setup"),
        ("__import__('bla')", "metaprogramming_in_setup"),
        ("from sys import modules, path", "metaprogramming_in_setup"),
        ("from sys import path, modules", "metaprogramming_in_setup"),
        ("import sys.modules", "metaprogramming_in_setup"),
        ("compile('malicious')", "metaprogramming_in_setup"),
        ("dir(someobject)", "metaprogramming_in_setup"),
        ("someobject.__dir__()", "metaprogramming_in_setup"),
        ("eval('malicious')", "metaprogramming_in_setup"),
        ("exec('malicious')", "metaprogramming_in_setup"),
        ("getattr(someobject, 'attr')", "metaprogramming_in_setup"),
        ("vars(someobject)", "metaprogramming_in_setup"),
        ("someobject.__dict__()", "metaprogramming_in_setup"),
        ("globals()", "metaprogramming_in_setup"),
        ("locals()", "metaprogramming_in_setup"),
        ("chr(42)", "metaprogramming_in_setup"),
        ("ord('x')", "metaprogramming_in_setup"),
        # alias imports
        (
            """
            import os as evil
            evil.system('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.system('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            hof = lambda evil, scary : evil(scary)
            hof(os.system, "cat /etc/passwd")
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.popen('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.popen('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.popen3('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.popen3('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.spawn('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.spawn('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.spawnve('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.spawnve('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.posix_spawn('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.posix_spawn('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.posix_spawnp('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.posix_spawnp('cat /etc/passwd')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os as evil
            evil.exec('malicious_code')
            """,
            "process_spawn_in_setup:metaprogramming_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.exec('malicious_code')
            """,
            "process_spawn_in_setup:metaprogramming_in_setup",
        ),
        (
            """
            import os as evil
            evil.execve('malicious_code')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import os
            evil = os
            evil.execve('malicious_code')
            """,
            "process_spawn_in_setup",
        ),
        (
            """
            import subprocess as evil
            evil.run('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess
            evil = subprocess
            evil.run('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess as evil
            evil.Popen('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess
            evil = subprocess
            evil.Popen('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess as evil
            evil.call('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess
            evil = subprocess
            evil.call('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess as evil
            evil.check_call('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess
            evil = subprocess
            evil.check_call('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess as evil
            evil.check_output('...')
            """,
            "subprocess_in_setup",
        ),
        (
            """
            import subprocess
            evil = subprocess
            evil.check_output('...')
            """,
            "subprocess_in_setup",
        ),
        # higher order functions, used for metaprogramming in setup.py
        (
            """
            hof = lambda evil : evil('malicious')
            hof(compile)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil(scary)
            hof(compile, 'malicious')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof('malicious', compile)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof('malicious',compile)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0]('malicious')
            hof([compile])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil[0](scary)
            hof([compile], 'malicious')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary[0](evil)
            hof('malicious', [compile])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']('malicious')
            hof({'mean' : compile})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']('malicious')
            hof({'mean':compile})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]('malicious')
            hof({compile:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]('malicious')
            hof({compile : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil(someobject)
            hof(dir)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil(scary)
            hof(dir, someobject)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof(someobject, dir)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof(someobject,dir)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0](someobject)
            hof([dir])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil[0](scary)
            hof([dir], someobject)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary[0](evil)
            hof(someobject, [dir])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean'](someobject)
            hof({'mean' : dir})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean'](someobject)
            hof({'mean':dir})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0](someobject)
            hof({dir:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0](someobject)
            hof({dir : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil('malicious')
            hof(eval)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil(scary)
            hof(eval, 'malicious')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof('malicious', eval)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof('malicious',eval)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0]('malicious')
            hof([eval])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil[0](scary)
            hof([eval], 'malicious')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary[0](evil)
            hof('malicious', [eval])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']('malicious')
            hof({'mean' : eval})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']('malicious')
            hof({'mean':eval})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]('malicious')
            hof({eval:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]('malicious')
            hof({eval : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil('malicious')
            hof(exec)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil(scary)
            hof(exec, 'malicious')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof('malicious', exec)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof('malicious',exec)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0]('malicious')
            hof([exec])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil[0](scary)
            hof([exec], 'malicious')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary[0](evil)
            hof('malicious', [exec])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']('malicious')
            hof({'mean' : exec})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']('malicious')
            hof({'mean':exec})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]('malicious')
            hof({exec:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]('malicious')
            hof({exec: 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil(someobject, 'attr')
            hof(getattr)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary, unkind : evil(scary, unkind)
            hof(getattr, someobject, 'attr')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda unkind, evil, scary : scary(evil, unkind)
            hof('attr', someobject, getattr)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda unkind, evil, scary : scary(evil, unkind)
            hof('attr', someobject,getattr)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0](someobject, 'attr')
            hof([getattr])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary, unkind : evil[0](scary, unkind)
            hof([getattr], someobject, 'attr')
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda unkind, evil, scary : scary[0](evil, unkind)
            hof('attr', someobject, [getattr])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean'](someobject, 'attr')
            hof({'mean' : getattr})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean'](someobject, 'attr')
            hof({'mean':getattr})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0](someobject, 'attr')
            hof({getattr:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0](someobject, 'attr')
            hof({getattr : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil(someobject)
            hof(vars)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil(scary)
            hof(vars, someobject)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof(someobject, vars)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary(evil)
            hof(someobject,vars)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0](someobject)
            hof([vars])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : evil[0](scary)
            hof([vars], someobject)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, scary : scary[0](evil)
            hof(someobject, [vars])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean'](someobject)
            hof({'mean' : vars})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean'](someobject)
            hof({'mean':vars})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0](someobject)
            hof({vars:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0](someobject)
            hof({vars : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil()
            hof(globals)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, _scary : evil()
            hof(globals, None)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda _evil, scary : scary()
            hof(None, globals)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda _evil, scary : scary()
            hof(None,globals)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0]()
            hof([globals])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']()
            hof({'mean' : globals})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']()
            hof({'mean':globals})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]()
            hof({globals:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]()
            hof({globals : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil()
            hof(locals)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil, _scary : evil()
            hof(locals, None)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda _evil, scary : scary()
            hof(None, locals)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda _evil, scary : scary()
            hof(None,locals)
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil[0]()
            hof([locals])
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']()
            hof({'mean' : locals})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : evil['mean']()
            hof({'mean':locals})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]()
            hof({locals:'bad'})
            """,
            "metaprogramming_in_setup",
        ),
        (
            """
            hof = lambda evil : list(evil.keys())[0]()
            hof({locals : 'bad'})
            """,
            "metaprogramming_in_setup",
        ),
    ],
)
def test_scan_matched_content(db_session, monkeypatch, malicious, rule):
    monkeypatch.setattr(
        c, "fetch_url_content", pretend.call_recorder(lambda *a: pretend.stub())
    )
    monkeypatch.setattr(
        c,
        "extract_file_content",
        pretend.call_recorder(
            lambda *a: b"this looks suspicious:\n" + malicious.encode("utf-8")
        ),
    )

    MalwareCheckFactory.create(
        name="SetupPatternCheck", state=MalwareCheckState.Enabled
    )
    check = c.SetupPatternCheck(db_session)

    file = FileFactory.create(packagetype="sdist")

    check.scan(obj=file, file_url=pretend.stub())

    assert len(check._verdicts) == 1
    assert check._verdicts[0].check_id == check.id
    assert check._verdicts[0].file_id == file.id
    threat_rules = {"process_spawn_in_setup", "subprocess_in_setup"}
    if set(rule.split(":")) & threat_rules:
        assert check._verdicts[0].classification == VerdictClassification.Threat
    else:
        assert check._verdicts[0].classification == VerdictClassification.Indeterminate
    assert check._verdicts[0].confidence == VerdictConfidence.High
    assert check._verdicts[0].message == rule
