Skip to content

Commit

Permalink
[ci] Fix flake8 offenses and standardize strings
Browse files Browse the repository at this point in the history
This commit addresses flake8 offenses identified by the linter.  It also
standardizes the formatting of multiline strings across all scripts,
ensuring consistency and improved readability.
  • Loading branch information
Ana06 committed Feb 4, 2025
1 parent cad4cc6 commit 8a16a61
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 56 deletions.
52 changes: 33 additions & 19 deletions virtualbox/vbox-adapter-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,31 @@
import textwrap

import gi
from vboxcommon import ensure_hostonlyif_exists, get_vm_state, run_vboxmanage

gi.require_version("Notify", "0.7")
from gi.repository import Notify
from vboxcommon import *
from gi.repository import Notify # noqa: E402

DYNAMIC_VM_NAME = ".dynamic"
DISABLED_ADAPTER_TYPE = "hostonly"
ALLOWED_ADAPTER_TYPES = ("hostonly", "intnet", "none")

DESCRIPTION = f"""Print the status of all internet adapters of all VMs in VirtualBox.
Notify if any VM with {DYNAMIC_VM_NAME} in the name has an adapter whose type is not allowed.
This is useful to detect internet access which is undesirable for dynamic malware analysis.
Optionally change the type of the adapters with non-allowed type to Host-Only."""

EPILOG = textwrap.dedent(
f"""
Example usage:
# Print status of all interfaces and disable internet access in VMs whose name contain {DYNAMIC_VM_NAME}
vbox-adapter-check.vm
# Print status of all interfaces without modifying any of them
vbox-adapter-check.vm --do_not_modify
"""
)


def get_vm_uuids(dynamic_only):
"""Gets the machine UUID(s) for a given VM name using 'VBoxManage list vms'."""
Expand All @@ -46,7 +62,7 @@ def get_vm_uuids(dynamic_only):
if (not dynamic_only) or DYNAMIC_VM_NAME in vm_name:
vm_uuids.append((vm_name, vm_uuid))
except Exception as e:
raise Exception(f"Error finding machines UUIDs") from e
raise Exception("Error finding machines UUIDs") from e
return vm_uuids


Expand All @@ -69,7 +85,7 @@ def change_network_adapters_to_hostonly(vm_uuid, vm_name, hostonly_ifname, do_no
# nic8="none"

vminfo = run_vboxmanage(["showvminfo", vm_uuid, "--machinereadable"])
for nic_number, nic_value in re.findall('^nic(\d+)="(\S+)"', vminfo, flags=re.M):
for nic_number, nic_value in re.findall(r'^nic(\d+)="(\S+)"', vminfo, flags=re.M):
if nic_value not in ALLOWED_ADAPTER_TYPES:
nics_with_internet.append(f"nic{nic_number}")
invalid_nics_msg += f"{nic_number} "
Expand All @@ -80,7 +96,11 @@ def change_network_adapters_to_hostonly(vm_uuid, vm_name, hostonly_ifname, do_no
if do_not_modify:
message = f"{vm_name} may be connected to the internet on adapter(s): {nic}. Please double check your VMs settings."
else:
message = f"{vm_name} may be connected to the internet on adapter(s): {nic}. The network adapter(s) have been disabled automatically to prevent an undesired internet connectivity. Please double check your VMs settings."
message = (
f"{vm_name} may be connected to the internet on adapter(s): {nic}."
"The network adapter(s) have been disabled automatically to prevent an undesired internet connectivity."
"Please double check your VMs settings."
)
# different commands are necessary if the machine is running.
if get_vm_state(vm_uuid) == "poweroff":
run_vboxmanage(
Expand All @@ -106,7 +126,11 @@ def change_network_adapters_to_hostonly(vm_uuid, vm_name, hostonly_ifname, do_no
if do_not_modify:
message = f"{vm_name} may be connected to the internet on adapter(s): {invalid_nics_msg}. Please double check your VMs settings."
else:
message = f"{vm_name} may be connected to the internet on adapter(s): {invalid_nics_msg}. The network adapter(s) have been disabled automatically to prevent an undesired internet connectivity. Please double check your VMs settings."
message = (
f"{vm_name} may be connected to the internet on adapter(s): {invalid_nics_msg}."
"The network adapter(s) have been disabled automatically to prevent an undesired internet connectivity."
"Please double check your VMs settings."
)

# Show notification using PyGObject
Notify.init("VirtualBox adapter check")
Expand All @@ -128,19 +152,9 @@ def main(argv=None):
if argv is None:
argv = sys.argv[1:]

epilog = textwrap.dedent(
f"""
Example usage:
# Print status of all internet adapters and disable the adapters with internet access in VMs with {DYNAMIC_VM_NAME} in the name
vbox-adapter-check.vm
# Print status of all internet adapters without modifying any of them
vbox-adapter-check.vm --do_not_modify
"""
)
parser = argparse.ArgumentParser(
description=f"Print the status of all internet adapters of all VMs in VirtualBox. Notify if any VM with {DYNAMIC_VM_NAME} in the name has an adapter whose type is not allowed (internet access is undesirable for dynamic malware analysis)i. Optionally change the type of the adapters with non-allowed type to Host-Only.",
epilog=epilog,
description=DESCRIPTION,
epilog=EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
Expand All @@ -162,7 +176,7 @@ def main(argv=None):
for vm_name, vm_uuid in vm_uuids:
change_network_adapters_to_hostonly(vm_uuid, vm_name, hostonly_ifname, args.do_not_modify)
else:
print(f"[Warning ⚠️] No VMs found")
print("[Warning ⚠️] No VMs found")
except Exception as e:
print(f"Error verifying dynamic VM hostonly configuration: {e}")

Expand Down
59 changes: 33 additions & 26 deletions virtualbox/vbox-clean-snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,30 @@
import sys
import textwrap

from vboxcommon import *
from vboxcommon import get_vm_state, run_vboxmanage

DESCRIPTION = "Clean a VirtualBox VM up by deleting a snapshot and its children recursively skipping snapshots with a substring in the name."

EPILOG = textwrap.dedent(
"""
Example usage:
# Delete all snapshots excluding the default protected ones (with 'clean' or 'done' in the name, case insensitive) in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604
# Delete all snapshots that do not include 'clean', 'done', or 'important' (case insensitive) in the name in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604 --protected_snapshots "clean,done,important"
# Delete the 'Snapshot 3' snapshot and its children recursively skipping the default protected ones in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604 --root_snapshot "Snapshot 3"
# Delete the 'CLEAN with IDA 8.4"' children snapshots recursively skipping the default protected ones in the 'FLARE-VM.20240604' VM
# NOTE: the 'CLEAN with IDA 8.4' root snapshot is skipped in this case
vbox-clean-snapshots.py FLARE-VM.20240604 --root_snapshot "CLEAN with IDA 8.4"
# Delete all snapshots in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604 --protected_snapshots ""
"""
)


def is_protected(protected_snapshots, snapshot_name):
Expand Down Expand Up @@ -60,7 +83,7 @@ def get_snapshot_children(vm_name, root_snapshot_name, protected_snapshots):
root_snapshot_index = ""
if root_snapshot_name:
# Find root snapshot: first snapshot with name root_snapshot_name (case sensitive)
root_snapshot_regex = f'^SnapshotName(?P<index>(?:-\d+)*)="{root_snapshot_name}"\n'
root_snapshot_regex = rf'^SnapshotName(?P<index>(?:-\d+)*)="{root_snapshot_name}"\n'
root_snapshot = re.search(root_snapshot_regex, snapshots_info, flags=re.M)
if root_snapshot:
root_snapshot_index = root_snapshot["index"]
Expand All @@ -69,7 +92,7 @@ def get_snapshot_children(vm_name, root_snapshot_name, protected_snapshots):

# Find all root and child snapshots as (snapshot_name, snapshot_id)
# Children of a snapshot share the same prefix index
index_regex = f"{root_snapshot_index}(?:-\d+)*"
index_regex = rf"{root_snapshot_index}(?:-\d+)*"
snapshot_regex = f'^SnapshotName{index_regex}="(.*?)"\nSnapshotUUID{index_regex}="(.*?)"'
snapshots = re.findall(snapshot_regex, snapshots_info, flags=re.M)

Expand Down Expand Up @@ -112,41 +135,25 @@ def main(argv=None):
if argv is None:
argv = sys.argv[1:]

epilog = textwrap.dedent(
"""
Example usage:
# Delete all snapshots that do not include 'clean' or 'done' in the name (case insensitive) in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604
# Delete all snapshots that do not include 'clean', 'done', or 'important in the name in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604 --protected_snapshots "clean,done,important"
# Delete the 'CLEAN with IDA 8.4' children snapshots recursively skipping the ones that include 'clean' or 'done' in the name (case insensitive) in the 'FLARE-VM.20240604' VM
# NOTE: the 'CLEAN with IDA 8.4' root snapshot is skipped in this case
vbox-clean-snapshots.py FLARE-VM.20240604 --root_snapshot CLEAN with IDA 8.4
# Delete the 'Snapshot 3' snapshot and its children recursively skipping the ones that include 'clean' or 'done' in the name (case insensitive) in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604 --root_snapshot Snapshot 3
# Delete all snapshots in the 'FLARE-VM.20240604' VM
vbox-clean-snapshots.py FLARE-VM.20240604 --protected_snapshots ""
"""
)
epilog = EPILOG
parser = argparse.ArgumentParser(
description="Clean a VirtualBox VM up by deleting a snapshot and its children recursively skipping snapshots with a substring in the name.",
description=DESCRIPTION,
epilog=epilog,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("vm_name", help="Name of the VM to clean up")
parser.add_argument(
"--root_snapshot",
help="Snapshot name (case sensitive) to delete (and its children recursively). Leave empty to clean all snapshots in the VM.",
help="""Snapshot name (case sensitive) to delete (and its children recursively).
Leave empty to clean all snapshots in the VM.""",
)
parser.add_argument(
"--protected_snapshots",
default="clean,done",
type=lambda s: s.split(","),
help='Comma-separated list of strings. Snapshots with any of the strings included in the name (case insensitive) are not deleted. Default: "clean,done"',
help='''Comma-separated list of strings.
Snapshots with any of the strings included in the name (case insensitive) are not deleted.
Default: "clean,done"''',
)
args = parser.parse_args(args=argv)

Expand Down
16 changes: 8 additions & 8 deletions virtualbox/vbox-export-snapshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import re
import sys
import textwrap
import time
from datetime import datetime

import jsonschema
from vboxcommon import *
from vboxcommon import ensure_hostonlyif_exists, ensure_vm_running, ensure_vm_shutdown, run_vboxmanage

DESCRIPTION = "Export one or more snapshots in the same VirtualBox VM as .ova, changing the network to a single Host-Only interface. Generate a file with the SHA256 of the exported OVA(s)."
DESCRIPTION = """Export one or more snapshots in the same VirtualBox VM as .ova, changing the network to a single Host-Only interface.
Generate a file with the SHA256 of the exported OVA(s)."""

EPILOG = textwrap.dedent(
"""
Expand Down Expand Up @@ -201,20 +203,18 @@ def main(argv=None):
)
parser.add_argument(
"config_path",
help=textwrap.dedent(
"""
path of the JSON configuration file.
help=""" path of the JSON configuration file.
"VM_NAME" is the name of the VM to export snapshots from.
Example: "FLARE-VM.testing".
"EXPORTED_VM_NAME" is the name of the exported VMs.
Example: "FLARE-VM".
"SNAPSHOTS" is a list of lists with information of the snapshots to export: ["SNAPSHOT_NAME", "EXPORTED_VM_EXTENSION", "DESCRIPTION"].
"SNAPSHOTS" is a list of lists with information of the snapshots to export:
["SNAPSHOT_NAME", "EXPORTED_VM_EXTENSION", "DESCRIPTION"].
Example: ["FLARE-VM", ".dynamic", "Windows 10 VM with FLARE-VM default configuration"].
"EXPORT_DIR_NAME" (optional) is the name of the directory in HOME to export the VMs.
The directory is created if it does not exist.
Default: "EXPORTED VMS".
"""
),
""",
)
args = parser.parse_args(args=argv)

Expand Down
6 changes: 3 additions & 3 deletions virtualbox/vboxcommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def get_hostonlyif_name():
# Name: vboxnet0
hostonlyifs_info = run_vboxmanage(["list", "hostonlyifs"])

match = re.search(f"^Name: *(?P<hostonlyif_name>\S+)", hostonlyifs_info, flags=re.M)
match = re.search(r"^Name: *(?P<hostonlyif_name>\S+)", hostonlyifs_info, flags=re.M)
if match:
return match["hostonlyif_name"]

Expand All @@ -73,7 +73,7 @@ def ensure_hostonlyif_exists():
if not hostonlyif_name:
raise RuntimeError("Failed to create new hostonly interface.")

print(f"VM {vm_uuid} Created hostonly interface: {hostonlyif_name}")
print(f"Hostonly interface created: {hostonlyif_name}")

return hostonlyif_name

Expand All @@ -84,7 +84,7 @@ def get_vm_state(vm_uuid):
# VMState="poweroff"
vm_info = run_vboxmanage(["showvminfo", vm_uuid, "--machinereadable"])

match = re.search(f'^VMState="(?P<state>\S+)"', vm_info, flags=re.M)
match = re.search(r'^VMState="(?P<state>\S+)"', vm_info, flags=re.M)
if match:
return match["state"]

Expand Down

0 comments on commit 8a16a61

Please sign in to comment.