mirror of
https://github.com/mvt-project/mvt.git
synced 2026-04-07 20:42:25 +02:00
Compare commits
22 Commits
feature/up
...
fix/stix2-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ba6d2ebb8 | ||
|
|
e80a05992e | ||
|
|
fd31f31aae | ||
|
|
47330e4e45 | ||
|
|
9305c655bb | ||
|
|
ec43f93eb9 | ||
|
|
7e398310b6 | ||
|
|
600e6dcf8f | ||
|
|
3d1407b78c | ||
|
|
d41ff6d604 | ||
|
|
ccd563f2ba | ||
|
|
c681d264b3 | ||
|
|
261b0ae000 | ||
|
|
89d30e84f4 | ||
|
|
557d0a0cd6 | ||
|
|
134bfce90f | ||
|
|
0141da4293 | ||
|
|
5cba61b180 | ||
|
|
29475acb47 | ||
|
|
1d5c83582c | ||
|
|
2dd1428787 | ||
|
|
f2d9f420f2 |
2
.github/workflows/add-issue-to-project.yml
vendored
2
.github/workflows/add-issue-to-project.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Add issue to project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v0.5.0
|
||||
- uses: actions/add-to-project@v1
|
||||
with:
|
||||
# You can target a project in a different organization
|
||||
# to the issue
|
||||
|
||||
7
.github/workflows/mypy.yml
vendored
7
.github/workflows/mypy.yml
vendored
@@ -7,14 +7,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install mypy
|
||||
|
||||
40
.github/workflows/publish-release-docker.yml
vendored
40
.github/workflows/publish-release-docker.yml
vendored
@@ -4,6 +4,8 @@ name: Create and publish a Docker image
|
||||
# Configures this workflow to run every time a release is published.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
@@ -23,9 +25,18 @@ jobs:
|
||||
attestations: write
|
||||
id-token: write
|
||||
#
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- dockerfile: "Dockerfile"
|
||||
tag-suffix: ""
|
||||
- dockerfile: "Dockerfile.ios"
|
||||
tag-suffix: "-ios"
|
||||
- dockerfile: "Dockerfile.android"
|
||||
tag-suffix: "-android"
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
@@ -36,26 +47,33 @@ jobs:
|
||||
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
flavor: |
|
||||
latest=false
|
||||
tags: |
|
||||
type=raw,value=latest,enable={{ is_default_branch }},suffix=${{ matrix.platform.tag-suffix }}
|
||||
type=raw,enable=${{ github.event_name == 'release' || github.ref_type == 'tag' }},value=stable,suffix=${{ matrix.platform.tag-suffix }}
|
||||
type=raw,enable=${{ github.event_name == 'release' }},value=${{ github.event.release.tag_name }},suffix=${{ matrix.platform.tag-suffix }}
|
||||
type=raw,enable=${{ github.ref_type == 'tag' }},value=${{ github.ref_name }},suffix=${{ matrix.platform.tag-suffix }}
|
||||
type=sha,suffix=${{ matrix.platform.tag-suffix }}
|
||||
type=sha,format=long,suffix=${{ matrix.platform.tag-suffix }}
|
||||
# This step sets up some additional capabilities to generate the provenance and sbom attestations
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
|
||||
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
|
||||
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
|
||||
- name: Build and push Docker image
|
||||
id: push
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
file: ${{ matrix.platform.dockerfile }}
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v1
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
provenance: mode=max
|
||||
sbom: true
|
||||
|
||||
|
||||
5
.github/workflows/ruff.yml
vendored
5
.github/workflows/ruff.yml
vendored
@@ -11,14 +11,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.9
|
||||
cache: 'pip'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install ruff
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -15,9 +15,9 @@ jobs:
|
||||
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Python dependencies
|
||||
|
||||
4
.github/workflows/update-ios-data.yml
vendored
4
.github/workflows/update-ios-data.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Run script to fetch latest iOS releases from Apple RSS feed.
|
||||
run: python3 .github/workflows/scripts/update-ios-releases.py
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
title: '[auto] Update iOS releases and versions'
|
||||
commit-message: Add new iOS versions and build numbers
|
||||
@@ -27,4 +27,4 @@ jobs:
|
||||
add-paths: |
|
||||
*.json
|
||||
labels: |
|
||||
automated pr
|
||||
automated pr
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@@ -1,6 +1,6 @@
|
||||
# Base image for building libraries
|
||||
# ---------------------------------
|
||||
FROM ubuntu:22.04 as build-base
|
||||
FROM ubuntu:22.04 AS build-base
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -22,7 +22,7 @@ RUN apt-get update \
|
||||
|
||||
# libplist
|
||||
# --------
|
||||
FROM build-base as build-libplist
|
||||
FROM build-base AS build-libplist
|
||||
|
||||
# Build
|
||||
RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
|
||||
@@ -32,7 +32,7 @@ RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
|
||||
|
||||
# libimobiledevice-glue
|
||||
# ---------------------
|
||||
FROM build-base as build-libimobiledevice-glue
|
||||
FROM build-base AS build-libimobiledevice-glue
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -45,7 +45,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice-glue && cd li
|
||||
|
||||
# libtatsu
|
||||
# --------
|
||||
FROM build-base as build-libtatsu
|
||||
FROM build-base AS build-libtatsu
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -58,7 +58,7 @@ RUN git clone https://github.com/libimobiledevice/libtatsu && cd libtatsu \
|
||||
|
||||
# libusbmuxd
|
||||
# ----------
|
||||
FROM build-base as build-libusbmuxd
|
||||
FROM build-base AS build-libusbmuxd
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -72,7 +72,7 @@ RUN git clone https://github.com/libimobiledevice/libusbmuxd && cd libusbmuxd \
|
||||
|
||||
# libimobiledevice
|
||||
# ----------------
|
||||
FROM build-base as build-libimobiledevice
|
||||
FROM build-base AS build-libimobiledevice
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -88,7 +88,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice && cd libimob
|
||||
|
||||
# usbmuxd
|
||||
# -------
|
||||
FROM build-base as build-usbmuxd
|
||||
FROM build-base AS build-usbmuxd
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -103,7 +103,7 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
|
||||
|
||||
|
||||
# Create main image
|
||||
FROM ubuntu:24.04 as main
|
||||
FROM ubuntu:24.04 AS main
|
||||
|
||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Create main image
|
||||
FROM python:3.10.14-alpine3.20 as main
|
||||
FROM python:3.10.14-alpine3.20 AS main
|
||||
|
||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Base image for building libraries
|
||||
# ---------------------------------
|
||||
FROM ubuntu:22.04 as build-base
|
||||
FROM ubuntu:22.04 AS build-base
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -22,7 +22,7 @@ RUN apt-get update \
|
||||
|
||||
# libplist
|
||||
# --------
|
||||
FROM build-base as build-libplist
|
||||
FROM build-base AS build-libplist
|
||||
|
||||
# Build
|
||||
RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
|
||||
@@ -32,7 +32,7 @@ RUN git clone https://github.com/libimobiledevice/libplist && cd libplist \
|
||||
|
||||
# libimobiledevice-glue
|
||||
# ---------------------
|
||||
FROM build-base as build-libimobiledevice-glue
|
||||
FROM build-base AS build-libimobiledevice-glue
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -45,7 +45,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice-glue && cd li
|
||||
|
||||
# libtatsu
|
||||
# --------
|
||||
FROM build-base as build-libtatsu
|
||||
FROM build-base AS build-libtatsu
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -58,7 +58,7 @@ RUN git clone https://github.com/libimobiledevice/libtatsu && cd libtatsu \
|
||||
|
||||
# libusbmuxd
|
||||
# ----------
|
||||
FROM build-base as build-libusbmuxd
|
||||
FROM build-base AS build-libusbmuxd
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -72,7 +72,7 @@ RUN git clone https://github.com/libimobiledevice/libusbmuxd && cd libusbmuxd \
|
||||
|
||||
# libimobiledevice
|
||||
# ----------------
|
||||
FROM build-base as build-libimobiledevice
|
||||
FROM build-base AS build-libimobiledevice
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -88,7 +88,7 @@ RUN git clone https://github.com/libimobiledevice/libimobiledevice && cd libimob
|
||||
|
||||
# usbmuxd
|
||||
# -------
|
||||
FROM build-base as build-usbmuxd
|
||||
FROM build-base AS build-usbmuxd
|
||||
|
||||
# Install dependencies
|
||||
COPY --from=build-libplist /build /
|
||||
@@ -104,7 +104,7 @@ RUN git clone https://github.com/libimobiledevice/usbmuxd && cd usbmuxd \
|
||||
|
||||
# Main image
|
||||
# ----------
|
||||
FROM python:3.10.14-alpine3.20 as main
|
||||
FROM python:3.10.14-alpine3.20 AS main
|
||||
|
||||
LABEL org.opencontainers.image.url="https://mvt.re"
|
||||
LABEL org.opencontainers.image.documentation="https://docs.mvt.re"
|
||||
|
||||
2
Makefile
2
Makefile
@@ -23,7 +23,7 @@ test-requirements:
|
||||
generate-proto-parsers:
|
||||
# Generate python parsers for protobuf files
|
||||
PROTO_FILES=$$(find src/mvt/android/parsers/proto/ -iname "*.proto"); \
|
||||
protoc -Isrc/mvt/android/parsers/proto/ --python_betterproto_out=src/mvt/android/parsers/proto/ $$PROTO_FILES
|
||||
protoc -Isrc/mvt/android/parsers/proto/ --python_betterproto2_out=src/mvt/android/parsers/proto/ $$PROTO_FILES
|
||||
|
||||
clean:
|
||||
rm -rf $(PWD)/build $(PWD)/dist $(PWD)/src/mvt.egg-info
|
||||
|
||||
@@ -2,4 +2,4 @@ mkdocs==1.6.1
|
||||
mkdocs-autorefs==1.4.3
|
||||
mkdocs-material==9.6.20
|
||||
mkdocs-material-extensions==1.3.1
|
||||
mkdocstrings==0.30.1
|
||||
mkdocstrings==1.0.0
|
||||
@@ -17,25 +17,25 @@ classifiers = [
|
||||
"Programming Language :: Python",
|
||||
]
|
||||
dependencies = [
|
||||
"click==8.3.0",
|
||||
"rich==14.1.0",
|
||||
"click==8.3.2",
|
||||
"rich==14.3.3",
|
||||
"tld==0.13.1",
|
||||
"requests==2.32.5",
|
||||
"requests==2.33.1",
|
||||
"simplejson==3.20.2",
|
||||
"packaging==25.0",
|
||||
"packaging==26.0",
|
||||
"appdirs==1.4.4",
|
||||
"iOSbackup==0.9.925",
|
||||
"adb-shell[usb]==0.4.4",
|
||||
"libusb1==3.3.1",
|
||||
"cryptography==46.0.3",
|
||||
"cryptography==46.0.6",
|
||||
"PyYAML>=6.0.2",
|
||||
"pyahocorasick==2.2.0",
|
||||
"betterproto==1.2.5",
|
||||
"pydantic==2.12.3",
|
||||
"pydantic-settings==2.10.1",
|
||||
"betterproto2==0.9.1",
|
||||
"pydantic==2.12.5",
|
||||
"pydantic-settings==2.13.1",
|
||||
"NSKeyedUnArchiver==1.5.2",
|
||||
"python-dateutil==2.9.0.post0",
|
||||
"tzdata==2025.2",
|
||||
"tzdata==2026.1",
|
||||
]
|
||||
requires-python = ">= 3.10"
|
||||
|
||||
@@ -57,7 +57,7 @@ dev = [
|
||||
"stix2>=3.0.1",
|
||||
"ruff>=0.1.6",
|
||||
"mypy>=1.7.1",
|
||||
"betterproto[compiler]",
|
||||
"betterproto2-compiler",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
@@ -81,8 +81,8 @@ addopts = "-ra -q --cov=mvt --cov-report html --junitxml=pytest.xml --cov-report
|
||||
testpaths = ["tests"]
|
||||
|
||||
[tool.ruff]
|
||||
select = ["C90", "E", "F", "W"] # flake8 default set
|
||||
ignore = [
|
||||
lint.select = ["C90", "E", "F", "W"] # flake8 default set
|
||||
lint.ignore = [
|
||||
"E501", # don't enforce line length violations
|
||||
"C901", # complex-structure
|
||||
|
||||
@@ -95,10 +95,10 @@ ignore = [
|
||||
# "E203", # whitespace-before-punctuation
|
||||
]
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["F401"] # unused-import
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
[tool.ruff.lint.mccabe]
|
||||
max-complexity = 10
|
||||
|
||||
[tool.setuptools]
|
||||
|
||||
@@ -7,7 +7,7 @@ import datetime
|
||||
from typing import List, Optional, Union
|
||||
|
||||
import pydantic
|
||||
import betterproto
|
||||
import betterproto2
|
||||
from dateutil import parser
|
||||
|
||||
from mvt.common.utils import convert_datetime_to_iso
|
||||
@@ -124,7 +124,7 @@ class TombstoneCrashArtifact(AndroidArtifact):
|
||||
"""Parse Android tombstone crash files from a protobuf object."""
|
||||
tombstone_pb = Tombstone().parse(data)
|
||||
tombstone_dict = tombstone_pb.to_dict(
|
||||
betterproto.Casing.SNAKE, include_default_values=True
|
||||
casing=betterproto2.Casing.SNAKE, include_default_values=True
|
||||
)
|
||||
|
||||
# Add some extra metadata
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# sources: tombstone.proto
|
||||
# plugin: python-betterproto
|
||||
# plugin: python-betterproto2
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, List
|
||||
|
||||
import betterproto
|
||||
import betterproto2
|
||||
|
||||
|
||||
class Architecture(betterproto.Enum):
|
||||
class Architecture(betterproto2.Enum):
|
||||
ARM32 = 0
|
||||
ARM64 = 1
|
||||
X86 = 2
|
||||
@@ -16,12 +15,12 @@ class Architecture(betterproto.Enum):
|
||||
NONE = 5
|
||||
|
||||
|
||||
class MemoryErrorTool(betterproto.Enum):
|
||||
class MemoryErrorTool(betterproto2.Enum):
|
||||
GWP_ASAN = 0
|
||||
SCUDO = 1
|
||||
|
||||
|
||||
class MemoryErrorType(betterproto.Enum):
|
||||
class MemoryErrorType(betterproto2.Enum):
|
||||
UNKNOWN = 0
|
||||
USE_AFTER_FREE = 1
|
||||
DOUBLE_FREE = 2
|
||||
@@ -30,179 +29,179 @@ class MemoryErrorType(betterproto.Enum):
|
||||
BUFFER_UNDERFLOW = 5
|
||||
|
||||
|
||||
@dataclass
|
||||
class CrashDetail(betterproto.Message):
|
||||
@dataclass(eq=False, repr=False)
|
||||
class CrashDetail(betterproto2.Message):
|
||||
"""
|
||||
NOTE TO OEMS: If you add custom fields to this proto, do not use numbers in
|
||||
the reserved range.
|
||||
"""
|
||||
|
||||
name: bytes = betterproto.bytes_field(1)
|
||||
data: bytes = betterproto.bytes_field(2)
|
||||
name: "bytes" = betterproto2.field(1, betterproto2.TYPE_BYTES)
|
||||
data: "bytes" = betterproto2.field(2, betterproto2.TYPE_BYTES)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StackHistoryBufferEntry(betterproto.Message):
|
||||
addr: "BacktraceFrame" = betterproto.message_field(1)
|
||||
fp: int = betterproto.uint64_field(2)
|
||||
tag: int = betterproto.uint64_field(3)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class StackHistoryBufferEntry(betterproto2.Message):
|
||||
addr: "BacktraceFrame | None" = betterproto2.field(1, betterproto2.TYPE_MESSAGE, optional=True)
|
||||
fp: "int" = betterproto2.field(2, betterproto2.TYPE_UINT64)
|
||||
tag: "int" = betterproto2.field(3, betterproto2.TYPE_UINT64)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StackHistoryBuffer(betterproto.Message):
|
||||
tid: int = betterproto.uint64_field(1)
|
||||
entries: List["StackHistoryBufferEntry"] = betterproto.message_field(2)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class StackHistoryBuffer(betterproto2.Message):
|
||||
tid: "int" = betterproto2.field(1, betterproto2.TYPE_UINT64)
|
||||
entries: "list[StackHistoryBufferEntry]" = betterproto2.field(2, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Tombstone(betterproto.Message):
|
||||
arch: "Architecture" = betterproto.enum_field(1)
|
||||
guest_arch: "Architecture" = betterproto.enum_field(24)
|
||||
build_fingerprint: str = betterproto.string_field(2)
|
||||
revision: str = betterproto.string_field(3)
|
||||
timestamp: str = betterproto.string_field(4)
|
||||
pid: int = betterproto.uint32_field(5)
|
||||
tid: int = betterproto.uint32_field(6)
|
||||
uid: int = betterproto.uint32_field(7)
|
||||
selinux_label: str = betterproto.string_field(8)
|
||||
command_line: List[str] = betterproto.string_field(9)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Tombstone(betterproto2.Message):
|
||||
arch: "Architecture" = betterproto2.field(1, betterproto2.TYPE_ENUM, default_factory=lambda: Architecture(0))
|
||||
guest_arch: "Architecture" = betterproto2.field(24, betterproto2.TYPE_ENUM, default_factory=lambda: Architecture(0))
|
||||
build_fingerprint: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
||||
revision: "str" = betterproto2.field(3, betterproto2.TYPE_STRING)
|
||||
timestamp: "str" = betterproto2.field(4, betterproto2.TYPE_STRING)
|
||||
pid: "int" = betterproto2.field(5, betterproto2.TYPE_UINT32)
|
||||
tid: "int" = betterproto2.field(6, betterproto2.TYPE_UINT32)
|
||||
uid: "int" = betterproto2.field(7, betterproto2.TYPE_UINT32)
|
||||
selinux_label: "str" = betterproto2.field(8, betterproto2.TYPE_STRING)
|
||||
command_line: "list[str]" = betterproto2.field(9, betterproto2.TYPE_STRING, repeated=True)
|
||||
# Process uptime in seconds.
|
||||
process_uptime: int = betterproto.uint32_field(20)
|
||||
signal_info: "Signal" = betterproto.message_field(10)
|
||||
abort_message: str = betterproto.string_field(14)
|
||||
crash_details: List["CrashDetail"] = betterproto.message_field(21)
|
||||
causes: List["Cause"] = betterproto.message_field(15)
|
||||
threads: Dict[int, "Thread"] = betterproto.map_field(
|
||||
16, betterproto.TYPE_UINT32, betterproto.TYPE_MESSAGE
|
||||
process_uptime: "int" = betterproto2.field(20, betterproto2.TYPE_UINT32)
|
||||
signal_info: "Signal | None" = betterproto2.field(10, betterproto2.TYPE_MESSAGE, optional=True)
|
||||
abort_message: "str" = betterproto2.field(14, betterproto2.TYPE_STRING)
|
||||
crash_details: "list[CrashDetail]" = betterproto2.field(21, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
causes: "list[Cause]" = betterproto2.field(15, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
threads: "dict[int, Thread]" = betterproto2.field(
|
||||
16, betterproto2.TYPE_MAP, map_meta=betterproto2.map_meta(betterproto2.TYPE_UINT32, betterproto2.TYPE_MESSAGE)
|
||||
)
|
||||
guest_threads: Dict[int, "Thread"] = betterproto.map_field(
|
||||
25, betterproto.TYPE_UINT32, betterproto.TYPE_MESSAGE
|
||||
guest_threads: "dict[int, Thread]" = betterproto2.field(
|
||||
25, betterproto2.TYPE_MAP, map_meta=betterproto2.map_meta(betterproto2.TYPE_UINT32, betterproto2.TYPE_MESSAGE)
|
||||
)
|
||||
memory_mappings: List["MemoryMapping"] = betterproto.message_field(17)
|
||||
log_buffers: List["LogBuffer"] = betterproto.message_field(18)
|
||||
open_fds: List["FD"] = betterproto.message_field(19)
|
||||
page_size: int = betterproto.uint32_field(22)
|
||||
has_been_16kb_mode: bool = betterproto.bool_field(23)
|
||||
stack_history_buffer: "StackHistoryBuffer" = betterproto.message_field(26)
|
||||
memory_mappings: "list[MemoryMapping]" = betterproto2.field(17, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
log_buffers: "list[LogBuffer]" = betterproto2.field(18, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
open_fds: "list[FD]" = betterproto2.field(19, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
page_size: "int" = betterproto2.field(22, betterproto2.TYPE_UINT32)
|
||||
has_been_16kb_mode: "bool" = betterproto2.field(23, betterproto2.TYPE_BOOL)
|
||||
stack_history_buffer: "StackHistoryBuffer | None" = betterproto2.field(26, betterproto2.TYPE_MESSAGE, optional=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Signal(betterproto.Message):
|
||||
number: int = betterproto.int32_field(1)
|
||||
name: str = betterproto.string_field(2)
|
||||
code: int = betterproto.int32_field(3)
|
||||
code_name: str = betterproto.string_field(4)
|
||||
has_sender: bool = betterproto.bool_field(5)
|
||||
sender_uid: int = betterproto.int32_field(6)
|
||||
sender_pid: int = betterproto.int32_field(7)
|
||||
has_fault_address: bool = betterproto.bool_field(8)
|
||||
fault_address: int = betterproto.uint64_field(9)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Signal(betterproto2.Message):
|
||||
number: "int" = betterproto2.field(1, betterproto2.TYPE_INT32)
|
||||
name: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
||||
code: "int" = betterproto2.field(3, betterproto2.TYPE_INT32)
|
||||
code_name: "str" = betterproto2.field(4, betterproto2.TYPE_STRING)
|
||||
has_sender: "bool" = betterproto2.field(5, betterproto2.TYPE_BOOL)
|
||||
sender_uid: "int" = betterproto2.field(6, betterproto2.TYPE_INT32)
|
||||
sender_pid: "int" = betterproto2.field(7, betterproto2.TYPE_INT32)
|
||||
has_fault_address: "bool" = betterproto2.field(8, betterproto2.TYPE_BOOL)
|
||||
fault_address: "int" = betterproto2.field(9, betterproto2.TYPE_UINT64)
|
||||
# Note, may or may not contain the dump of the actual memory contents.
|
||||
# Currently, on arm64, we only include metadata, and not the contents.
|
||||
fault_adjacent_metadata: "MemoryDump" = betterproto.message_field(10)
|
||||
fault_adjacent_metadata: "MemoryDump | None" = betterproto2.field(10, betterproto2.TYPE_MESSAGE, optional=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class HeapObject(betterproto.Message):
|
||||
address: int = betterproto.uint64_field(1)
|
||||
size: int = betterproto.uint64_field(2)
|
||||
allocation_tid: int = betterproto.uint64_field(3)
|
||||
allocation_backtrace: List["BacktraceFrame"] = betterproto.message_field(4)
|
||||
deallocation_tid: int = betterproto.uint64_field(5)
|
||||
deallocation_backtrace: List["BacktraceFrame"] = betterproto.message_field(6)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class HeapObject(betterproto2.Message):
|
||||
address: "int" = betterproto2.field(1, betterproto2.TYPE_UINT64)
|
||||
size: "int" = betterproto2.field(2, betterproto2.TYPE_UINT64)
|
||||
allocation_tid: "int" = betterproto2.field(3, betterproto2.TYPE_UINT64)
|
||||
allocation_backtrace: "list[BacktraceFrame]" = betterproto2.field(4, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
deallocation_tid: "int" = betterproto2.field(5, betterproto2.TYPE_UINT64)
|
||||
deallocation_backtrace: "list[BacktraceFrame]" = betterproto2.field(6, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemoryError(betterproto.Message):
|
||||
tool: "MemoryErrorTool" = betterproto.enum_field(1)
|
||||
type: "MemoryErrorType" = betterproto.enum_field(2)
|
||||
heap: "HeapObject" = betterproto.message_field(3, group="location")
|
||||
@dataclass(eq=False, repr=False)
|
||||
class MemoryError(betterproto2.Message):
|
||||
tool: "MemoryErrorTool" = betterproto2.field(1, betterproto2.TYPE_ENUM, default_factory=lambda: MemoryErrorTool(0))
|
||||
type: "MemoryErrorType" = betterproto2.field(2, betterproto2.TYPE_ENUM, default_factory=lambda: MemoryErrorType(0))
|
||||
heap: "HeapObject | None" = betterproto2.field(3, betterproto2.TYPE_MESSAGE, optional=True, group="location")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Cause(betterproto.Message):
|
||||
human_readable: str = betterproto.string_field(1)
|
||||
memory_error: "MemoryError" = betterproto.message_field(2, group="details")
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Cause(betterproto2.Message):
|
||||
human_readable: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
||||
memory_error: "MemoryError | None" = betterproto2.field(2, betterproto2.TYPE_MESSAGE, optional=True, group="details")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Register(betterproto.Message):
|
||||
name: str = betterproto.string_field(1)
|
||||
u64: int = betterproto.uint64_field(2)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Register(betterproto2.Message):
|
||||
name: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
||||
u64: "int" = betterproto2.field(2, betterproto2.TYPE_UINT64)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Thread(betterproto.Message):
|
||||
id: int = betterproto.int32_field(1)
|
||||
name: str = betterproto.string_field(2)
|
||||
registers: List["Register"] = betterproto.message_field(3)
|
||||
backtrace_note: List[str] = betterproto.string_field(7)
|
||||
unreadable_elf_files: List[str] = betterproto.string_field(9)
|
||||
current_backtrace: List["BacktraceFrame"] = betterproto.message_field(4)
|
||||
memory_dump: List["MemoryDump"] = betterproto.message_field(5)
|
||||
tagged_addr_ctrl: int = betterproto.int64_field(6)
|
||||
pac_enabled_keys: int = betterproto.int64_field(8)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Thread(betterproto2.Message):
|
||||
id: "int" = betterproto2.field(1, betterproto2.TYPE_INT32)
|
||||
name: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
||||
registers: "list[Register]" = betterproto2.field(3, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
backtrace_note: "list[str]" = betterproto2.field(7, betterproto2.TYPE_STRING, repeated=True)
|
||||
unreadable_elf_files: "list[str]" = betterproto2.field(9, betterproto2.TYPE_STRING, repeated=True)
|
||||
current_backtrace: "list[BacktraceFrame]" = betterproto2.field(4, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
memory_dump: "list[MemoryDump]" = betterproto2.field(5, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
tagged_addr_ctrl: "int" = betterproto2.field(6, betterproto2.TYPE_INT64)
|
||||
pac_enabled_keys: "int" = betterproto2.field(8, betterproto2.TYPE_INT64)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BacktraceFrame(betterproto.Message):
|
||||
rel_pc: int = betterproto.uint64_field(1)
|
||||
pc: int = betterproto.uint64_field(2)
|
||||
sp: int = betterproto.uint64_field(3)
|
||||
function_name: str = betterproto.string_field(4)
|
||||
function_offset: int = betterproto.uint64_field(5)
|
||||
file_name: str = betterproto.string_field(6)
|
||||
file_map_offset: int = betterproto.uint64_field(7)
|
||||
build_id: str = betterproto.string_field(8)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class BacktraceFrame(betterproto2.Message):
|
||||
rel_pc: "int" = betterproto2.field(1, betterproto2.TYPE_UINT64)
|
||||
pc: "int" = betterproto2.field(2, betterproto2.TYPE_UINT64)
|
||||
sp: "int" = betterproto2.field(3, betterproto2.TYPE_UINT64)
|
||||
function_name: "str" = betterproto2.field(4, betterproto2.TYPE_STRING)
|
||||
function_offset: "int" = betterproto2.field(5, betterproto2.TYPE_UINT64)
|
||||
file_name: "str" = betterproto2.field(6, betterproto2.TYPE_STRING)
|
||||
file_map_offset: "int" = betterproto2.field(7, betterproto2.TYPE_UINT64)
|
||||
build_id: "str" = betterproto2.field(8, betterproto2.TYPE_STRING)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ArmMTEMetadata(betterproto.Message):
|
||||
@dataclass(eq=False, repr=False)
|
||||
class ArmMTEMetadata(betterproto2.Message):
|
||||
# One memory tag per granule (e.g. every 16 bytes) of regular memory.
|
||||
memory_tags: bytes = betterproto.bytes_field(1)
|
||||
memory_tags: "bytes" = betterproto2.field(1, betterproto2.TYPE_BYTES)
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemoryDump(betterproto.Message):
|
||||
register_name: str = betterproto.string_field(1)
|
||||
mapping_name: str = betterproto.string_field(2)
|
||||
begin_address: int = betterproto.uint64_field(3)
|
||||
memory: bytes = betterproto.bytes_field(4)
|
||||
arm_mte_metadata: "ArmMTEMetadata" = betterproto.message_field(6, group="metadata")
|
||||
@dataclass(eq=False, repr=False)
|
||||
class MemoryDump(betterproto2.Message):
|
||||
register_name: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
||||
mapping_name: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
||||
begin_address: "int" = betterproto2.field(3, betterproto2.TYPE_UINT64)
|
||||
memory: "bytes" = betterproto2.field(4, betterproto2.TYPE_BYTES)
|
||||
arm_mte_metadata: "ArmMTEMetadata | None" = betterproto2.field(6, betterproto2.TYPE_MESSAGE, optional=True, group="metadata")
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemoryMapping(betterproto.Message):
|
||||
begin_address: int = betterproto.uint64_field(1)
|
||||
end_address: int = betterproto.uint64_field(2)
|
||||
offset: int = betterproto.uint64_field(3)
|
||||
read: bool = betterproto.bool_field(4)
|
||||
write: bool = betterproto.bool_field(5)
|
||||
execute: bool = betterproto.bool_field(6)
|
||||
mapping_name: str = betterproto.string_field(7)
|
||||
build_id: str = betterproto.string_field(8)
|
||||
load_bias: int = betterproto.uint64_field(9)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class MemoryMapping(betterproto2.Message):
|
||||
begin_address: "int" = betterproto2.field(1, betterproto2.TYPE_UINT64)
|
||||
end_address: "int" = betterproto2.field(2, betterproto2.TYPE_UINT64)
|
||||
offset: "int" = betterproto2.field(3, betterproto2.TYPE_UINT64)
|
||||
read: "bool" = betterproto2.field(4, betterproto2.TYPE_BOOL)
|
||||
write: "bool" = betterproto2.field(5, betterproto2.TYPE_BOOL)
|
||||
execute: "bool" = betterproto2.field(6, betterproto2.TYPE_BOOL)
|
||||
mapping_name: "str" = betterproto2.field(7, betterproto2.TYPE_STRING)
|
||||
build_id: "str" = betterproto2.field(8, betterproto2.TYPE_STRING)
|
||||
load_bias: "int" = betterproto2.field(9, betterproto2.TYPE_UINT64)
|
||||
|
||||
|
||||
@dataclass
|
||||
class FD(betterproto.Message):
|
||||
fd: int = betterproto.int32_field(1)
|
||||
path: str = betterproto.string_field(2)
|
||||
owner: str = betterproto.string_field(3)
|
||||
tag: int = betterproto.uint64_field(4)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class FD(betterproto2.Message):
|
||||
fd: "int" = betterproto2.field(1, betterproto2.TYPE_INT32)
|
||||
path: "str" = betterproto2.field(2, betterproto2.TYPE_STRING)
|
||||
owner: "str" = betterproto2.field(3, betterproto2.TYPE_STRING)
|
||||
tag: "int" = betterproto2.field(4, betterproto2.TYPE_UINT64)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LogBuffer(betterproto.Message):
|
||||
name: str = betterproto.string_field(1)
|
||||
logs: List["LogMessage"] = betterproto.message_field(2)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class LogBuffer(betterproto2.Message):
|
||||
name: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
||||
logs: "list[LogMessage]" = betterproto2.field(2, betterproto2.TYPE_MESSAGE, repeated=True)
|
||||
|
||||
|
||||
@dataclass
|
||||
class LogMessage(betterproto.Message):
|
||||
timestamp: str = betterproto.string_field(1)
|
||||
pid: int = betterproto.uint32_field(2)
|
||||
tid: int = betterproto.uint32_field(3)
|
||||
priority: int = betterproto.uint32_field(4)
|
||||
tag: str = betterproto.string_field(5)
|
||||
message: str = betterproto.string_field(6)
|
||||
@dataclass(eq=False, repr=False)
|
||||
class LogMessage(betterproto2.Message):
|
||||
timestamp: "str" = betterproto2.field(1, betterproto2.TYPE_STRING)
|
||||
pid: "int" = betterproto2.field(2, betterproto2.TYPE_UINT32)
|
||||
tid: "int" = betterproto2.field(3, betterproto2.TYPE_UINT32)
|
||||
priority: "int" = betterproto2.field(4, betterproto2.TYPE_UINT32)
|
||||
tag: "str" = betterproto2.field(5, betterproto2.TYPE_STRING)
|
||||
message: "str" = betterproto2.field(6, betterproto2.TYPE_STRING)
|
||||
|
||||
@@ -52,9 +52,7 @@ class Indicators:
|
||||
if os.path.isfile(path) and path.lower().endswith(".stix2"):
|
||||
self.parse_stix2(path)
|
||||
elif os.path.isdir(path):
|
||||
for file in glob.glob(
|
||||
os.path.join(path, "**", "*.stix2", recursive=True)
|
||||
):
|
||||
for file in glob.glob(os.path.join(path, "**", "*.stix2"), recursive=True):
|
||||
self.parse_stix2(file)
|
||||
else:
|
||||
self.log.error(
|
||||
@@ -102,6 +100,17 @@ class Indicators:
|
||||
key, value = indicator.get("pattern", "").strip("[]").split("=")
|
||||
key = key.strip()
|
||||
|
||||
# Normalize hash algorithm keys so that both the STIX2-spec-compliant
|
||||
# form (e.g. file:hashes.'SHA-256', which requires quotes around
|
||||
# algorithm names that contain hyphens) and the non-standard lowercase
|
||||
# form (e.g. file:hashes.sha256) are accepted. Strip single quotes and
|
||||
# hyphens from the algorithm name only, then lowercase it.
|
||||
for sep in ("hashes.", "cert."):
|
||||
if sep in key:
|
||||
prefix, _, algo = key.partition(sep)
|
||||
key = prefix + sep + algo.replace("'", "").replace("-", "").lower()
|
||||
break
|
||||
|
||||
if key == "domain-name:value":
|
||||
# We force domain names to lower case.
|
||||
self._add_indicator(
|
||||
|
||||
@@ -907,6 +907,10 @@
|
||||
"version": "15.8.6",
|
||||
"build": "19H402"
|
||||
},
|
||||
{
|
||||
"version": "15.8.7",
|
||||
"build": "19H411"
|
||||
},
|
||||
{
|
||||
"build": "20A362",
|
||||
"version": "16.0"
|
||||
@@ -1020,6 +1024,10 @@
|
||||
"version": "16.7.14",
|
||||
"build": "20H370"
|
||||
},
|
||||
{
|
||||
"version": "16.7.15",
|
||||
"build": "20H380"
|
||||
},
|
||||
{
|
||||
"version": "17.0",
|
||||
"build": "21A327"
|
||||
@@ -1188,6 +1196,10 @@
|
||||
"version": "18.7.6",
|
||||
"build": "22H320"
|
||||
},
|
||||
{
|
||||
"version": "18.7.7",
|
||||
"build": "22H333"
|
||||
},
|
||||
{
|
||||
"version": "26",
|
||||
"build": "23A341"
|
||||
@@ -1215,5 +1227,9 @@
|
||||
{
|
||||
"version": "26.3.1",
|
||||
"build": "23D8133"
|
||||
},
|
||||
{
|
||||
"version": "26.4",
|
||||
"build": "23E246"
|
||||
}
|
||||
]
|
||||
@@ -82,7 +82,7 @@ def generate_test_stix_file(file_path):
|
||||
for h in sha256:
|
||||
i = Indicator(
|
||||
indicator_types=["malicious-activity"],
|
||||
pattern="[file:hashes.sha256='{}']".format(h),
|
||||
pattern="[file:hashes.'SHA-256'='{}']".format(h),
|
||||
pattern_type="stix",
|
||||
)
|
||||
res.append(i)
|
||||
@@ -91,7 +91,7 @@ def generate_test_stix_file(file_path):
|
||||
for h in sha1:
|
||||
i = Indicator(
|
||||
indicator_types=["malicious-activity"],
|
||||
pattern="[file:hashes.sha1='{}']".format(h),
|
||||
pattern="[file:hashes.'SHA-1'='{}']".format(h),
|
||||
pattern_type="stix",
|
||||
)
|
||||
res.append(i)
|
||||
|
||||
@@ -94,6 +94,78 @@ class TestIndicators:
|
||||
)
|
||||
assert ind.check_file_hash("da0611a300a9ce9aa7a09d1212f203fca5856794")
|
||||
|
||||
def test_parse_stix2_hash_key_variants(self, tmp_path):
|
||||
"""STIX2 spec requires single-quoted algorithm names that contain hyphens,
|
||||
e.g. file:hashes.'SHA-256'. Verify MVT accepts both spec-compliant and
|
||||
non-standard lowercase spellings for MD5, SHA-1 and SHA-256."""
|
||||
import json
|
||||
|
||||
sha256_hash = "570cd76bf49cf52e0cb347a68bdcf0590b2eaece134e1b1eba7e8d66261bdbe6"
|
||||
sha1_hash = "da0611a300a9ce9aa7a09d1212f203fca5856794"
|
||||
md5_hash = "d41d8cd98f00b204e9800998ecf8427e"
|
||||
|
||||
variants = [
|
||||
# (pattern_key, expected_bucket)
|
||||
("file:hashes.'SHA-256'", "files_sha256"),
|
||||
("file:hashes.SHA-256", "files_sha256"),
|
||||
("file:hashes.SHA256", "files_sha256"),
|
||||
("file:hashes.sha256", "files_sha256"),
|
||||
("file:hashes.'SHA-1'", "files_sha1"),
|
||||
("file:hashes.SHA-1", "files_sha1"),
|
||||
("file:hashes.SHA1", "files_sha1"),
|
||||
("file:hashes.sha1", "files_sha1"),
|
||||
("file:hashes.MD5", "files_md5"),
|
||||
("file:hashes.'MD5'", "files_md5"),
|
||||
("file:hashes.md5", "files_md5"),
|
||||
]
|
||||
|
||||
hash_for = {
|
||||
"files_sha256": sha256_hash,
|
||||
"files_sha1": sha1_hash,
|
||||
"files_md5": md5_hash,
|
||||
}
|
||||
|
||||
for pattern_key, bucket in variants:
|
||||
h = hash_for[bucket]
|
||||
stix = {
|
||||
"type": "bundle",
|
||||
"id": "bundle--test",
|
||||
"objects": [
|
||||
{
|
||||
"type": "malware",
|
||||
"id": "malware--test",
|
||||
"name": "TestMalware",
|
||||
"is_family": False,
|
||||
},
|
||||
{
|
||||
"type": "indicator",
|
||||
"id": "indicator--test",
|
||||
"indicator_types": ["malicious-activity"],
|
||||
"pattern": f"[{pattern_key}='{h}']",
|
||||
"pattern_type": "stix",
|
||||
"valid_from": "2024-01-01T00:00:00Z",
|
||||
},
|
||||
{
|
||||
"type": "relationship",
|
||||
"id": "relationship--test",
|
||||
"relationship_type": "indicates",
|
||||
"source_ref": "indicator--test",
|
||||
"target_ref": "malware--test",
|
||||
},
|
||||
],
|
||||
}
|
||||
stix_file = tmp_path / "test.stix2"
|
||||
stix_file.write_text(json.dumps(stix))
|
||||
|
||||
ind = Indicators(log=logging)
|
||||
ind.load_indicators_files([str(stix_file)], load_default=False)
|
||||
assert len(ind.ioc_collections[0][bucket]) == 1, (
|
||||
f"Pattern key '{pattern_key}' was not parsed into '{bucket}'"
|
||||
)
|
||||
assert ind.check_file_hash(h) is not None, (
|
||||
f"check_file_hash failed for pattern key '{pattern_key}'"
|
||||
)
|
||||
|
||||
def test_check_android_property(self, indicator_file):
|
||||
ind = Indicators(log=logging)
|
||||
ind.load_indicators_files([indicator_file], load_default=False)
|
||||
|
||||
Reference in New Issue
Block a user