Merge pull request #480 from Ana06/py3-only

This commit is contained in:
Ana María Martínez Gómez
2021-04-08 10:48:15 +02:00
committed by GitHub
28 changed files with 127 additions and 221 deletions

View File

@@ -15,7 +15,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '2.7'
python-version: '3.6'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
@@ -27,3 +27,4 @@ jobs:
run: |
python setup.py sdist bdist_wheel
twine upload --skip-existing dist/*

View File

@@ -52,8 +52,6 @@ jobs:
python-version: [3.6, 3.9]
include:
# on Ubuntu run these as well
- os: ubuntu-20.04
python-version: 2.7
- os: ubuntu-20.04
python-version: 3.7
- os: ubuntu-20.04

View File

@@ -2,6 +2,8 @@
## master (unreleased)
The first Python 3 ONLY capa version.
### New Features
### New Rules
@@ -10,6 +12,8 @@
### Changes
- py3: drop Python 2 support #480 @Ana06
### Development
### Raw diffs

View File

@@ -27,10 +27,7 @@ VALID_ARCH = (ARCH_X32, ARCH_X64)
def bytes_to_str(b):
if sys.version_info[0] >= 3:
return str(codecs.encode(b, "hex").decode("utf-8"))
else:
return codecs.encode(b, "hex")
return str(codecs.encode(b, "hex").decode("utf-8"))
def hex_string(h):

View File

@@ -16,10 +16,7 @@ MIN_STACKSTRING_LEN = 8
def xor_static(data, i):
if sys.version_info >= (3, 0):
return bytes(c ^ i for c in data)
else:
return "".join(chr(ord(c) ^ i) for c in data)
return bytes(c ^ i for c in data)
def is_aw_function(symbol):

View File

@@ -34,10 +34,7 @@ def add_ea_int_cast(o):
this bit of skullduggery lets use cast viv-utils objects as ints.
the correct way of doing this is to update viv-utils (or subclass the objects here).
"""
if sys.version_info[0] >= 3:
setattr(o, "__int__", types.MethodType(get_ea, o))
else:
setattr(o, "__int__", types.MethodType(get_ea, o, type(o)))
setattr(o, "__int__", types.MethodType(get_ea, o))
return o

View File

@@ -39,18 +39,11 @@ def get_printable_len(op):
raise ValueError("Unhandled operand data type 0x%x." % op.dtype)
def is_printable_ascii(chars):
if sys.version_info[0] >= 3:
return all(c < 127 and chr(c) in string.printable for c in chars)
else:
return all(ord(c) < 127 and c in string.printable for c in chars)
return all(c < 127 and chr(c) in string.printable for c in chars)
def is_printable_utf16le(chars):
if sys.version_info[0] >= 3:
if all(c == 0x00 for c in chars[1::2]):
return is_printable_ascii(chars[::2])
else:
if all(c == "\x00" for c in chars[1::2]):
return is_printable_ascii(chars[::2])
if all(c == 0x00 for c in chars[1::2]):
return is_printable_ascii(chars[::2])
if is_printable_ascii(chars):
return idaapi.get_dtype_size(op.dtype)

View File

@@ -23,11 +23,7 @@ def find_byte_sequence(start, end, seq):
end: max virtual address
seq: bytes to search e.g. b"\x01\x03"
"""
if sys.version_info[0] >= 3:
seq = " ".join(["%02x" % b for b in seq])
else:
seq = " ".join(["%02x" % ord(b) for b in seq])
seq = " ".join(["%02x" % b for b in seq])
while True:
ea = idaapi.find_binary(start, end, seq, 0, idaapi.SEARCH_DOWN)
if ea == idaapi.BADADDR:

View File

@@ -15,8 +15,6 @@ from capa.features.extractors import FeatureExtractor
class SmdaFeatureExtractor(FeatureExtractor):
def __init__(self, smda_report: SmdaReport, path):
super(SmdaFeatureExtractor, self).__init__()
if sys.version_info < (3, 0):
raise UnsupportedRuntimeError("SMDA should only be used with Python 3.")
self.smda_report = smda_report
self.path = path

View File

@@ -264,15 +264,14 @@ def main(argv=None):
parser.add_argument(
"-f", "--format", choices=[f[0] for f in formats], default="auto", help="Select sample format, %s" % format_help
)
if sys.version_info >= (3, 0):
parser.add_argument(
"-b",
"--backend",
type=str,
help="select the backend to use",
choices=(capa.main.BACKEND_VIV, capa.main.BACKEND_SMDA),
default=capa.main.BACKEND_VIV,
)
parser.add_argument(
"-b",
"--backend",
type=str,
help="select the backend to use",
choices=(capa.main.BACKEND_VIV, capa.main.BACKEND_SMDA),
default=capa.main.BACKEND_VIV,
)
args = parser.parse_args(args=argv)
if args.quiet:
@@ -285,8 +284,7 @@ def main(argv=None):
logging.basicConfig(level=logging.INFO)
logging.getLogger().setLevel(logging.INFO)
backend = args.backend if sys.version_info > (3, 0) else capa.main.BACKEND_VIV
extractor = capa.main.get_extractor(args.sample, args.format, backend)
extractor = capa.main.get_extractor(args.sample, args.format, args.backend)
with open(args.output, "wb") as f:
f.write(dump(extractor))

View File

@@ -12,9 +12,7 @@ _hex = hex
def hex(i):
# under py2.7, long integers get formatted with a trailing `L`
# and this is not pretty. so strip it out.
return _hex(oint(i)).rstrip("L")
return _hex(oint(i))
def oint(i):

View File

@@ -10,7 +10,6 @@ import logging
import datetime
import idc
import six
import idaapi
import idautils
@@ -85,7 +84,7 @@ def get_func_start_ea(ea):
def get_file_md5():
""" """
md5 = idautils.GetInputFileMD5()
if not isinstance(md5, six.string_types):
if not isinstance(md5, str):
md5 = capa.features.bytes_to_str(md5)
return md5
@@ -93,7 +92,7 @@ def get_file_md5():
def get_file_sha256():
""" """
sha256 = idaapi.retrieve_input_file_sha256()
if not isinstance(sha256, six.string_types):
if not isinstance(sha256, str):
sha256 = capa.features.bytes_to_str(sha256)
return sha256

View File

@@ -36,7 +36,7 @@ For more information on the FLARE team's open-source framework, capa, check out
capa explorer supports the following IDA setups:
* IDA Pro 7.4+ with Python 2.7 or Python 3.
* IDA Pro 7.4+ with Python >= 3.6.
If you encounter issues with your specific setup, please open a new [Issue](https://github.com/fireeye/capa/issues).

View File

@@ -328,14 +328,10 @@ class CapaExplorerByteViewItem(CapaExplorerFeatureItem):
"""
byte_snap = idaapi.get_bytes(location, 32)
details = ""
if byte_snap:
byte_snap = codecs.encode(byte_snap, "hex").upper()
if sys.version_info >= (3, 0):
details = " ".join([byte_snap[i : i + 2].decode() for i in range(0, len(byte_snap), 2)])
else:
details = " ".join([byte_snap[i : i + 2] for i in range(0, len(byte_snap), 2)])
else:
details = ""
details = " ".join([byte_snap[i : i + 2].decode() for i in range(0, len(byte_snap), 2)])
super(CapaExplorerByteViewItem, self).__init__(parent, display, location=location, details=details)
self.ida_highlight = idc.get_color(location, idc.CIC_ITEM)

View File

@@ -5,7 +5,6 @@
# 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 six
from PyQt5 import QtCore
from PyQt5.QtCore import Qt
@@ -208,7 +207,7 @@ class CapaExplorerSearchProxyModel(QtCore.QSortFilterProxyModel):
if not data:
continue
if not isinstance(data, six.string_types):
if not isinstance(data, str):
# sanity check: should already be a string, but double check
continue

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
"""
Copyright (C) 2020 FireEye, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -288,26 +288,15 @@ def get_workspace(path, format, should_save=True):
return vw
def get_extractor_py2(path, format, disable_progress=False):
import capa.features.extractors.viv
with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress):
vw = get_workspace(path, format, should_save=False)
try:
vw.saveWorkspace()
except IOError:
# see #168 for discussion around how to handle non-writable directories
logger.info("source directory is not writable, won't save intermediate workspace")
return capa.features.extractors.viv.VivisectFeatureExtractor(vw, path)
class UnsupportedRuntimeError(RuntimeError):
pass
def get_extractor_py3(path, format, backend, disable_progress=False):
def get_extractor(path, format, backend, disable_progress=False):
"""
raises:
UnsupportedFormatError:
"""
if backend == "smda":
from smda.SmdaConfig import SmdaConfig
from smda.Disassembler import Disassembler
@@ -337,17 +326,6 @@ def get_extractor_py3(path, format, backend, disable_progress=False):
return capa.features.extractors.viv.VivisectFeatureExtractor(vw, path)
def get_extractor(path, format, backend, disable_progress=False):
"""
raises:
UnsupportedFormatError:
"""
if sys.version_info >= (3, 0):
return get_extractor_py3(path, format, backend, disable_progress=disable_progress)
else:
return get_extractor_py2(path, format, disable_progress=disable_progress)
def is_nursery_rule_path(path):
"""
The nursery is a spot for rules that have not yet been fully polished.
@@ -460,7 +438,7 @@ def install_common_args(parser, wanted=None):
wanted (Set[str]): collection of arguments to opt-into, including:
- "sample": required positional argument to input file.
- "format": flag to override file format.
- "backend": flag to override analysis backend under py3.
- "backend": flag to override analysis backend.
- "rules": flag to override path to capa rules.
- "tag": flag to override/specify which rules to match.
"""
@@ -498,22 +476,11 @@ def install_common_args(parser, wanted=None):
#
if "sample" in wanted:
if sys.version_info >= (3, 0):
parser.add_argument(
# Python 3 str handles non-ASCII arguments correctly
"sample",
type=str,
help="path to sample to analyze",
)
else:
parser.add_argument(
# in #328 we noticed that the sample path is not handled correctly if it contains non-ASCII characters
# https://stackoverflow.com/a/22947334/ offers a solution and decoding using getfilesystemencoding works
# in our testing, however other sources suggest `sys.stdin.encoding` (https://stackoverflow.com/q/4012571/)
"sample",
type=lambda s: s.decode(sys.getfilesystemencoding()),
help="path to sample to analyze",
)
parser.add_argument(
"sample",
type=str,
help="path to sample to analyze",
)
if "format" in wanted:
formats = [
@@ -532,15 +499,15 @@ def install_common_args(parser, wanted=None):
help="select sample format, %s" % format_help,
)
if "backend" in wanted and sys.version_info >= (3, 0):
parser.add_argument(
"-b",
"--backend",
type=str,
help="select the backend to use",
choices=(BACKEND_VIV, BACKEND_SMDA),
default=BACKEND_VIV,
)
if "backend" in wanted:
parser.add_argument(
"-b",
"--backend",
type=str,
help="select the backend to use",
choices=(BACKEND_VIV, BACKEND_SMDA),
default=BACKEND_VIV,
)
if "rules" in wanted:
parser.add_argument(
@@ -576,10 +543,9 @@ def handle_common_args(args):
# disable vivisect-related logging, it's verbose and not relevant for capa users
set_vivisect_log_level(logging.CRITICAL)
# py2 doesn't know about cp65001, which is a variant of utf-8 on windows
# tqdm bails when trying to render the progress bar in this setup.
# because cp65001 is utf-8, we just map that codepage to the utf-8 codec.
# see #380 and: https://stackoverflow.com/a/3259271/87207
# Since Python 3.8 cp65001 is an alias to utf_8, but not for Pyhton < 3.8
# TODO: remove this code when only supporting Python 3.8+
# https://stackoverflow.com/a/3259271/87207
import codecs
codecs.register(lambda name: codecs.lookup("utf-8") if name == "cp65001" else None)
@@ -599,6 +565,9 @@ def handle_common_args(args):
def main(argv=None):
if sys.version_info < (3, 6):
raise UnsupportedRuntimeError("This version of capa can only be used with Python 3.6+")
if argv is None:
argv = sys.argv[1:]
@@ -703,8 +672,7 @@ def main(argv=None):
else:
format = args.format
try:
backend = args.backend if sys.version_info > (3, 0) else BACKEND_VIV
extractor = get_extractor(args.sample, args.format, backend, disable_progress=args.quiet)
extractor = get_extractor(args.sample, args.format, args.backend, disable_progress=args.quiet)
except UnsupportedFormatError:
logger.error("-" * 80)
logger.error(" Input file does not appear to be a PE file.")
@@ -715,16 +683,6 @@ def main(argv=None):
logger.error(" If you don't know the input file type, you can try using the `file` utility to guess it.")
logger.error("-" * 80)
return -1
except UnsupportedRuntimeError:
logger.error("-" * 80)
logger.error(" Unsupported runtime or Python interpreter.")
logger.error(" ")
logger.error(" capa supports running under Python 2.7 using Vivisect for binary analysis.")
logger.error(" It can also run within IDA Pro, using either Python 2.7 or 3.5+.")
logger.error(" ")
logger.error(" If you're seeing this message on the command line, please ensure you're running Python 2.7.")
logger.error("-" * 80)
return -1
meta = collect_metadata(argv, args.sample, args.rules, format, extractor)

View File

@@ -8,8 +8,6 @@
import json
import six
import capa.rules
import capa.engine
@@ -249,7 +247,7 @@ class CapaJsonObjectEncoder(json.JSONEncoder):
"""JSON encoder that emits Python sets as sorted lists"""
def default(self, obj):
if isinstance(obj, (list, dict, int, float, bool, type(None))) or isinstance(obj, six.string_types):
if isinstance(obj, (list, dict, int, float, bool, type(None))) or isinstance(obj, str):
return json.JSONEncoder.default(self, obj)
elif isinstance(obj, set):
return list(sorted(obj))

View File

@@ -8,7 +8,6 @@
import collections
import six
import tabulate
import capa.render.utils as rutils

View File

@@ -6,7 +6,8 @@
# 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 six
import io
import termcolor
@@ -49,7 +50,7 @@ def capability_rules(doc):
yield rule
class StringIO(six.StringIO):
class StringIO(io.StringIO):
def writeln(self, s):
self.write(s)
self.write("\n")

View File

@@ -18,7 +18,8 @@ try:
except ImportError:
from backports.functools_lru_cache import lru_cache
import six
import io
import yaml
import ruamel.yaml
@@ -244,7 +245,7 @@ def parse_description(s, value_type, description=None):
"""
s can be an int or a string
"""
if value_type != "string" and isinstance(s, six.string_types) and DESCRIPTION_SEPARATOR in s:
if value_type != "string" and isinstance(s, str) and DESCRIPTION_SEPARATOR in s:
if description:
raise InvalidRule(
'unexpected value: "%s", only one description allowed (inline description with `%s`)'
@@ -256,12 +257,11 @@ def parse_description(s, value_type, description=None):
else:
value = s
if isinstance(value, six.string_types):
if isinstance(value, str):
if value_type == "bytes":
try:
value = codecs.decode(value.replace(" ", ""), "hex")
# TODO: Remove TypeError when Python2 is not used anymore
except (TypeError, binascii.Error):
except binascii.Error:
raise InvalidRule('unexpected bytes value: "%s", must be a valid hex sequence' % value)
if len(value) > MAX_BYTES_FEATURE_SIZE:
@@ -406,7 +406,7 @@ def build_statements(d, scope):
return Range(feature, min=min, max=max, description=description)
else:
raise InvalidRule("unexpected range: %s" % (count))
elif key == "string" and not isinstance(d[key], six.string_types):
elif key == "string" and not isinstance(d[key], str):
raise InvalidRule("ambiguous string value %s, must be defined as explicit string" % d[key])
else:
Feature = parse_feature(key)
@@ -699,7 +699,7 @@ class Rule(object):
for key in hidden_meta.keys():
del meta[key]
ostream = six.BytesIO()
ostream = io.BytesIO()
self._get_ruamel_yaml_parser().dump(definition, ostream)
for key, value in hidden_meta.items():
@@ -938,7 +938,7 @@ class RuleSet(object):
rules_filtered = set([])
for rule in rules:
for k, v in rule.meta.items():
if isinstance(v, six.string_types) and tag in v:
if isinstance(v, str) and tag in v:
logger.debug('using rule "%s" and dependencies, found tag in meta.%s: %s', rule.name, k, v)
rules_filtered.update(set(capa.rules.get_rules_and_dependencies(rules, rule.name)))
break

View File

@@ -59,6 +59,25 @@ Use `pip` to install the source code in "editable" mode. This means that Python
You'll find that the `capa.exe` (Windows) or `capa` (Linux/MacOS) executables in your path now invoke the capa binary from this directory.
#### Development
##### venv [optional]
For development, we recommend to use [venv](https://docs.python.org/3/tutorial/venv.html). It allows you to create a virtual environment: a self-contained directory tree that contains a Python installation for a particular version of Python, plus a number of additional packages. This approach avoids conflicts between the requirements of different applications on your computer. It also ensures that you don't overlook to add a new requirement to `setup.up` using a library already installed on your system.
To create an environment (in the parent directory, to avoid commiting it by accident or messing with the linters), run:
`$ python3 -m venv ../capa-env`
To activate `capa-env` in Linux or MacOS, run:
`$ source ../capa-env/bin/activate`
To activate `capa-env` in Windows, run:
`$ ..\capa-env\Scripts\activate.bat`
For more details about creating and using virtual environments, check out the [venv documentation](https://docs.python.org/3/tutorial/venv.html).
##### Install development dependencies
We use the following tools to ensure consistent code style and formatting:
- [black](https://github.com/psf/black) code formatter, with `-l 120`
- [isort 5](https://pypi.org/project/isort/) code formatter, with `--profile black --length-sort --line-width 120`
@@ -69,11 +88,21 @@ To install these development dependencies, run:
`$ pip install -e /local/path/to/src[dev]`
Note that some development dependencies (including the black code formatter) require Python 3.
To check the code style, formatting and run the tests you can run the script `scripts/ci.sh`.
You can run it with the argument `no_tests` to skip the tests and only run the code style and formatting: `scripts/ci.sh no_tests`
##### Setup hooks [optional]
If you plan to contribute to capa, you may want to setup the hooks.
Run `scripts/setup-hooks.sh` to set the following hooks up:
- The `pre-commit` hook runs checks before every `git commit`.
It runs `scripts/ci.sh no_tests` aborting the commit if there are code style or rule linter offenses you need to fix.
- The `pre-push` hook runs checks before every `git push`.
It runs `scripts/ci.sh` aborting the push if there are code style or rule linter offenses or if the tests fail.
This way you can ensure everything is alright before sending a pull request.
You can skip the checks by using the `--no-verify` git option.
### 3. Compile binary using PyInstaller
We compile capa standalone binaries using PyInstaller. To reproduce the build process check out the source code as described above and follow these steps.
@@ -86,13 +115,3 @@ For Python 3: `$ pip install 'pyinstaller`
`$ pyinstaller .github/pyinstaller/pyinstaller.spec`
You can find the compiled binary in the created directory `dist/`.
### 4. Setup hooks [optional]
If you plan to contribute to capa, you may want to setup the hooks.
Run `scripts/setup-hooks.sh` to set the following hooks up:
- The `pre-commit` hook runs checks before every `git commit`.
It runs `scripts/ci.sh no_tests` aborting the commit if there are code style or rule linter offenses you need to fix.
You can skip this check by using the `--no-verify` git option.
- The `pre-push` hook runs checks before every `git push`.
It runs `scripts/ci.sh` aborting the push if there are code style or rule linter offenses or if the tests fail.
This way you can ensure everything is alright before sending a pull request.

View File

@@ -9,6 +9,7 @@
# See the License for the specific language governing permissions and limitations under the License.
# Use a console with emojis support for a better experience
# Use venv to ensure that `python` calls the correct python version
# Stash uncommited changes
MSG="pre-push-$(date +%s)";
@@ -25,17 +26,8 @@ restore_stashed() {
fi
}
python_3() {
case "$(uname -s)" in
CYGWIN*|MINGW32*|MSYS*|MINGW*)
py -3 -m $1 > $2 2>&1;;
*)
python3 -m $1 > $2 2>&1;;
esac
}
# Run isort and print state
python_3 'isort --profile black --length-sort --line-width 120 -c .' 'isort-output.log';
python -m isort --profile black --length-sort --line-width 120 -c . > isort-output.log 2>&1;
if [ $? == 0 ]; then
echo 'isort succeeded!! 💖';
else
@@ -46,7 +38,7 @@ else
fi
# Run black and print state
python_3 'black -l 120 --check .' 'black-output.log';
python -m black -l 120 --check . > black-output.log 2>&1;
if [ $? == 0 ]; then
echo 'black succeeded!! 💝';
else
@@ -70,7 +62,7 @@ fi
# Run tests except if first argument is no_tests
if [ "$1" != 'no_tests' ]; then
echo 'Running tests, please wait ⌛';
pytest tests/ --maxfail=1;
python -m pytest tests/ --maxfail=1;
if [ $? == 0 ]; then
echo 'Tests succeed!! 🎉';
else

View File

@@ -12,7 +12,6 @@ import sys
import setuptools
requirements = [
"six==1.15.0",
"tqdm==4.60.0",
"pyyaml==5.4.1",
"tabulate==0.8.9",
@@ -21,24 +20,13 @@ requirements = [
"wcwidth==0.2.5",
"ida-settings==2.1.0",
"viv-utils==0.6.0",
"halo==0.0.31",
"networkx==2.5.1",
"ruamel.yaml==0.17.0",
"vivisect==1.0.1",
"smda==1.5.13",
]
if sys.version_info >= (3, 0):
# py3
requirements.append("halo==0.0.31")
requirements.append("networkx==2.5.1")
requirements.append("ruamel.yaml==0.17.0")
requirements.append("vivisect==1.0.1")
requirements.append("smda==1.5.13")
else:
# py2
requirements.append("enum34==1.1.6") # v1.1.6 is needed by halo 0.0.30 / spinners 0.0.24
requirements.append("halo==0.0.30") # halo==0.0.30 is the last version to support py2.7
requirements.append("vivisect==0.2.1")
requirements.append("networkx==2.2") # v2.2 is last version supported by Python 2.7
requirements.append("ruamel.yaml==0.16.13") # last version tested with Python 2.7
requirements.append("backports.functools-lru-cache==1.6.1")
# this sets __version__
# via: http://stackoverflow.com/a/7071358/87207
# and: http://stackoverflow.com/a/2073599/87207
@@ -77,13 +65,13 @@ setuptools.setup(
install_requires=requirements,
extras_require={
"dev": [
"pytest==4.6.11", # TODO: Change to 6.2.3 when removing py2
"pytest==6.2.3",
"pytest-sugar==0.9.4",
"pytest-instafail==0.4.2",
"pytest-cov==2.11.1",
"pycodestyle==2.7.0",
"black==20.8b1 ; python_version>'3.0'",
"isort==4.3.21", # TODO: Change to 5.8.0 when removing py2
"black==20.8b1",
"isort==5.8.0",
]
},
zip_safe=False,
@@ -94,8 +82,8 @@ setuptools.setup(
"Intended Audience :: Information Technology",
"License :: OSI Approved :: Apache Software License",
"Natural Language :: English",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Topic :: Security",
],
python_requires=">=3.6",
)

View File

@@ -8,7 +8,6 @@
# See the License for the specific language governing permissions and limitations under the License.
import os
import sys
import os.path
import binascii
import contextlib

View File

@@ -5,7 +5,6 @@
# 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 sys
import textwrap
import pytest
@@ -174,7 +173,6 @@ def test_serialize_features():
roundtrip_feature(capa.features.file.Import("#11"))
@pytest.mark.xfail(sys.version_info >= (3, 0), reason="vivsect only works on py2")
def test_freeze_sample(tmpdir, z9324d_extractor):
# tmpdir fixture handles cleanup
o = tmpdir.mkdir("capa").join("test.frz").strpath
@@ -182,7 +180,6 @@ def test_freeze_sample(tmpdir, z9324d_extractor):
assert capa.features.freeze.main([path, o, "-v"]) == 0
@pytest.mark.xfail(sys.version_info >= (3, 0), reason="vivsect only works on py2")
def test_freeze_load_sample(tmpdir, z9324d_extractor):
o = tmpdir.mkdir("capa").join("test.frz")

View File

@@ -12,8 +12,6 @@ from capa.features.extractors import helpers
def test_all_zeros():
# Python 2: <str>
# Python 3: <bytes>
a = b"\x00\x00\x00\x00"
b = codecs.decode("00000000", "hex")
c = b"\x01\x00\x00\x00"

View File

@@ -6,7 +6,6 @@
# 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 sys
import json
import textwrap
@@ -58,10 +57,6 @@ def test_main_single_rule(z9324d_extractor, tmpdir):
def test_main_non_ascii_filename(pingtaest_extractor, tmpdir, capsys):
# on py2.7, need to be careful about str (which can hold bytes)
# vs unicode (which is only unicode characters).
# on py3, this should not be needed.
#
# here we print a string with unicode characters in it
# (specifically, a byte string with utf-8 bytes in it, see file encoding)
assert capa.main.main(["-q", pingtaest_extractor.path]) == 0
@@ -69,20 +64,14 @@ def test_main_non_ascii_filename(pingtaest_extractor, tmpdir, capsys):
std = capsys.readouterr()
# but here, we have to use a unicode instance,
# because capsys has decoded the output for us.
if sys.version_info >= (3, 0):
assert pingtaest_extractor.path in std.out
else:
assert pingtaest_extractor.path.decode("utf-8") in std.out
assert pingtaest_extractor.path in std.out
def test_main_non_ascii_filename_nonexistent(tmpdir, caplog):
NON_ASCII_FILENAME = "täst_not_there.exe"
assert capa.main.main(["-q", NON_ASCII_FILENAME]) == -1
if sys.version_info >= (3, 0):
assert NON_ASCII_FILENAME in caplog.text
else:
assert NON_ASCII_FILENAME.decode("utf-8") in caplog.text
assert NON_ASCII_FILENAME in caplog.text
def test_main_shellcode(z499c2_extractor):
@@ -370,16 +359,15 @@ def test_not_render_rules_also_matched(z9324d_extractor, capsys):
# It tests main works with different backends
def test_backend_option(capsys):
if sys.version_info > (3, 0):
path = get_data_path_by_name("pma16-01")
assert capa.main.main([path, "-j", "-b", capa.main.BACKEND_VIV]) == 0
std = capsys.readouterr()
std_json = json.loads(std.out)
assert std_json["meta"]["analysis"]["extractor"] == "VivisectFeatureExtractor"
assert len(std_json["rules"]) > 0
path = get_data_path_by_name("pma16-01")
assert capa.main.main([path, "-j", "-b", capa.main.BACKEND_VIV]) == 0
std = capsys.readouterr()
std_json = json.loads(std.out)
assert std_json["meta"]["analysis"]["extractor"] == "VivisectFeatureExtractor"
assert len(std_json["rules"]) > 0
assert capa.main.main([path, "-j", "-b", capa.main.BACKEND_SMDA]) == 0
std = capsys.readouterr()
std_json = json.loads(std.out)
assert std_json["meta"]["analysis"]["extractor"] == "SmdaFeatureExtractor"
assert len(std_json["rules"]) > 0
assert capa.main.main([path, "-j", "-b", capa.main.BACKEND_SMDA]) == 0
std = capsys.readouterr()
std_json = json.loads(std.out)
assert std_json["meta"]["analysis"]["extractor"] == "SmdaFeatureExtractor"
assert len(std_json["rules"]) > 0

View File

@@ -15,7 +15,6 @@ from fixtures import *
FEATURE_PRESENCE_TESTS,
indirect=["sample", "scope"],
)
@pytest.mark.xfail(sys.version_info < (3, 0), reason="SMDA only works on py3")
@pytest.mark.xfail(sys.platform == "win32", reason="SMDA bug: https://github.com/danielplohmann/smda/issues/20")
def test_smda_features(sample, scope, feature, expected):
do_test_feature_presence(get_smda_extractor, sample, scope, feature, expected)
@@ -27,5 +26,4 @@ def test_smda_features(sample, scope, feature, expected):
indirect=["sample", "scope"],
)
def test_smda_feature_counts(sample, scope, feature, expected):
with xfail(sys.version_info < (3, 0), reason="SMDA only works on py3"):
do_test_feature_count(get_smda_extractor, sample, scope, feature, expected)
do_test_feature_count(get_smda_extractor, sample, scope, feature, expected)