Bump version: v0.21.0 (#1220)

This commit is contained in:
Paweł Srokosz
2026-08-19 17:36:21 +02:00
committed by GitHub
parent b196c7ce20
commit 7c328ab48b
8 changed files with 293 additions and 44 deletions
+2 -1
View File
@@ -27,7 +27,7 @@ copyright = '2025, CERT Polska'
author = 'CERT Polska'
# The full version, including alpha/beta/rc tags
release = 'v0.20.0'
release = 'v0.21.0'
latex_engine = 'xelatex'
@@ -41,6 +41,7 @@ def setup(app):
# ones.
extensions = [
"sphinx_rtd_theme",
"sphinx.ext.autosectionlabel",
"sphinx.ext.graphviz",
]
+30
View File
@@ -2,6 +2,36 @@
What's changed, how to upgrade?
===============================
v0.21.0
-------
This release comes with multiple bugfixes and additional web application features.
Most important changes:
- Web: All analysis files can be downloaded as .zip archive
- Added offline HTML analysis reports that can be downloaded and shared with someone without direct access to Drakvuf Sandbox
- Support for "shellexec" injection method and runas elevation
- Improved iptables rules, added conntrack drop after analysis
Known issues:
- Analysis may hang on Windows 7 with pre-installed Powershell 2.0. It's recommended to upgrade Powershell to at least 5.1. See also `the issue #1199 on Github <https://github.com/CERT-Polska/drakvuf-sandbox/issues/1199>`_.
This version was tested using `DRAKVUF v1.1-8107f115 <https://github.com/tklengyel/drakvuf/commit/8107f115d35311d483e314f48c3b172e094ec6e9>`_ and may not work correctly with older versions.
It's recommended to upgrade DRAKVUF to this version or newer before running analyses.
After upgrading Drakvuf engine, it's also recommended to regenerate VMI profiles to include the 64-bit .NET ``clr.dll`` profile. This can be
done by doing the following operations:
- shut down all of the workers and ensure that all of the guest VMs are stopped
- run ``drakrun modify-vm0 begin`` to start VM-0
- after ensuring that VM started correctly, run ``drakrun modify-vm0 commit`` to create a new snapshot and refresh the VMI profiles.
It's recommended to read the :ref:`Managing snapshots` documentation section before running these operations.
Complete changelog can be found here: `v0.21.0 changelog <https://github.com/CERT-Polska/drakvuf-sandbox/releases/tag/v0.21.0>`_.
v0.20.0
-------
+1 -1
View File
@@ -1 +1 @@
__version__ = "0.20.0"
__version__ = "0.21.0"
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@drakvuf-sandbox/web",
"version": "0.20.0",
"version": "0.21.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@drakvuf-sandbox/web",
"version": "0.20.0",
"version": "0.21.0",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-regular-svg-icons": "^6.7.2",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@drakvuf-sandbox/web",
"private": true,
"version": "0.20.0",
"version": "0.21.0",
"type": "module",
"scripts": {
"dev": "vite",
-9
View File
@@ -1,9 +0,0 @@
{
"files": {
"drakrun/web/frontend/package.json": "\"version\": \"$VERSION\"",
"drakrun/web/frontend/package-lock.json": "\"version\": \"$VERSION\"",
"drakrun/version.py": "__version__ = \"$VERSION\"",
"docs/conf.py": "release = 'v$VERSION'"
},
"regex": "(\\d+\\.\\d+\\.\\d+(?:-(post|dev|alpha|beta|rc)[0-9]?)?)"
}
+244 -30
View File
@@ -1,27 +1,161 @@
#! /usr/bin/env python3
# bump_version.py 0.1.0
# Copyright (c) 2026, CERT Polska. All rights reserved.
#
# This file's content is free software; you can redistribute and/or
# modify it under the terms of the *BSD 3-Clause "New" or "Revised"
#
# bump_version 0.2.0 - Utility for version bump and management
#
# This file automates the version bumping in various files and
# assists CI workflow in determining the version information.
#
# Usage: python3 bump_version.py --help
import argparse
import difflib
import json
import itertools
import tomllib
import re
import sys
from pathlib import Path
CONFIG = json.loads((Path(__file__).parent / "bump_version.json").read_text())
CURRENT_DIR = Path.cwd()
SCRIPT_ROOT = Path(__file__).parent
PROJECT_ROOT = SCRIPT_ROOT.parent
CONFIG = tomllib.loads((SCRIPT_ROOT / "bump_version.toml").read_text())
VERSION_FILES = {
(CURRENT_DIR / path): pattern for path, pattern in CONFIG["files"].items()
(PROJECT_ROOT / path): patterns for path, patterns in CONFIG["files"].items()
}
VERSION_REGEX = CONFIG["regex"]
# https://packaging.python.org/en/latest/specifications/version-specifiers/#version-specifiers
VERSION_REGEX = (
r"(?P<version>"
r"(?P<major>\d+)[.]"
r"(?P<minor>\d+)[.]"
r"(?P<patch>\d+)"
r"(?P<pre_release>(?:a|b|rc)\d+)?"
r"(?P<post_release>(?:.post)\d+)?"
r"(?P<dev_release>(?:.dev)\d+)?"
r")"
)
CHANGELOG_FILE = PROJECT_ROOT / "CHANGELOG.md"
def main(new_version):
def get_changelog_header_pattern() -> str:
"""Load and validate changelog header from configuration"""
header_pattern = CONFIG.get("changelog", {}).get("header_pattern")
if not header_pattern:
raise RuntimeError(
"[changelog].header_pattern is undefined in bump_version.toml"
)
if not "$VERSION" in header_pattern:
raise RuntimeError("[changelog].header_pattern must have $VERSION mark")
return header_pattern
def load_changelog() -> str:
"""Load changelog file contents"""
if not CHANGELOG_FILE.exists():
raise RuntimeError(f"Changelog file '{CHANGELOG_FILE}' doesn't exist")
return CHANGELOG_FILE.read_text()
def extract_changelog() -> dict[str, str]:
"""Load changelog entries for each version"""
changelog = load_changelog()
header_pattern = get_changelog_header_pattern()
version_pattern = header_pattern.replace("$VERSION", VERSION_REGEX)
matches = re.finditer(version_pattern, changelog)
entries = {}
for cur_version, next_version in itertools.pairwise(
itertools.chain(matches, [None])
):
start = changelog.index("\n", cur_version.end())
end = (
len(changelog)
if not next_version
else changelog.rindex("\n", start, next_version.start())
)
entries[cur_version.group("version")] = changelog[start:end].strip()
return entries
def find_latest_version() -> str:
"""Find current latest version from version files"""
current_version = None
for path in VERSION_FILES.keys():
if not path.exists():
raise RuntimeError(f"File {path} is missing in project root dir?")
content = path.read_text()
for pattern in VERSION_FILES[path]:
pattern = pattern.replace("$VERSION", VERSION_REGEX)
version = next(re.finditer(pattern, content)).group("version")
if current_version is not None and version != current_version:
raise RuntimeError(
f"{path} contains different version than other files "
f"({version} != {current_version})"
)
current_version = version
if not current_version:
raise RuntimeError("Version files not found or undefined")
return current_version
def increment_version(version: str, major: bool, minor: bool, patch: bool) -> str:
"""
Increment version number.
Raises RuntimeError when version is a pre-release or dev-release.
Major, minor and patch flags are treated as mutually exclusive.
:param version: Version number to increment
:param major: Increment major
:param minor: Increment minor
:param patch: Increment patch
:return: Incremented version number
"""
version_match = re.fullmatch(VERSION_REGEX, version)
if not version_match:
raise RuntimeError(f"'{version}' doesn't match the regex: {VERSION_REGEX}")
if version_match.group("pre_release") or version_match.group("dev_release"):
raise RuntimeError(
f"'{version}' is a pre-release version, please provide version manually"
)
vmajor = int(version_match.group("major"))
vminor = int(version_match.group("minor"))
vpatch = int(version_match.group("patch"))
if major:
vmajor = vmajor + 1
vminor = vpatch = 0
elif minor:
vminor = vminor + 1
vpatch = 0
elif patch:
vpatch = vpatch + 1
return f"{vmajor}.{vminor}.{vpatch}"
def bump_version(new_version: str) -> None:
"""
Interactively bumps version, prompting user for confirmation
:param new_version: New version specified by user
"""
input_files = {}
output_files = {}
old_version = None
if not re.match(fr"^{VERSION_REGEX}$", new_version):
print(f"[!] '{new_version}' doesn't match the regex: {VERSION_REGEX}")
return
if not re.fullmatch(VERSION_REGEX, new_version):
raise RuntimeError(f"'{new_version}' doesn't match the regex: {VERSION_REGEX}")
def subst_version(repl):
return (
@@ -32,34 +166,55 @@ def main(new_version):
for path in VERSION_FILES.keys():
if not path.exists():
print(f"[!] File {str(path)} is missing. Are you in project root dir?")
return False
raise RuntimeError(f"File {path} is missing in project root dir?")
with open(path, "r") as f:
content = input_files[path] = f.read()
content = input_files[path] = path.read_text()
pattern = VERSION_FILES[path].replace("$VERSION", VERSION_REGEX)
print(pattern)
version = next(re.finditer(pattern, content)).group(1)
output_files[path] = re.sub(pattern, subst_version, content, count=1)
for pattern in VERSION_FILES[path]:
pattern = pattern.replace("$VERSION", VERSION_REGEX)
version = next(re.finditer(pattern, content)).group("version")
content = re.sub(pattern, subst_version, content)
output_files[path] = content
if old_version is not None and version != old_version:
print(
f"[!] {str(path)} contains different version than other files "
f"({version} != {old_version})"
if old_version is not None and version != old_version:
raise RuntimeError(
f"{path} contains different version than other files "
f"({version} != {old_version})"
)
old_version = version
if CHANGELOG_FILE.exists():
changelog = load_changelog()
header_pattern = get_changelog_header_pattern()
changelog_entries = extract_changelog()
if new_version not in changelog_entries:
if "new_header_pattern" in CONFIG.get("changelog", {}):
version_header = CONFIG["changelog"]["new_header_pattern"].replace(
"$VERSION", new_version
)
else:
version_header = header_pattern.replace("$VERSION", new_version)
input_files[CHANGELOG_FILE] = changelog
output_files[CHANGELOG_FILE] = (
changelog.rstrip() + "\n\n" + version_header + "\n\n"
)
old_version = version
for path in VERSION_FILES.keys():
changes = False
for path in output_files.keys():
input_lines = input_files[path].splitlines()
output_lines = output_files[path].splitlines()
if input_lines == output_lines:
print("[*] No changes detected.")
return
continue
changes = True
print("=== " + str(path))
for line in difflib.unified_diff(input_lines, output_lines, lineterm=""):
print(line)
if not changes:
print("[*] No changes detected.")
return
response = ""
while response.lower() not in {"y", "n", "yes", "no"}:
response = input("[*] Check above diff ^ Is it correct? (y/n): ")
@@ -69,12 +224,71 @@ def main(new_version):
with open(path, "w") as f:
f.write(content)
print("[+] Changes applied!")
if CHANGELOG_FILE in output_files:
print(
"[+] New changelog entry was created. Make sure to fill it before applying version bump."
)
else:
print("[-] Changes discarded.")
def parse_args(args: tuple[str, ...]) -> argparse.Namespace:
parser = argparse.ArgumentParser(prog=Path(__file__).name)
subparsers = parser.add_subparsers(dest="command")
bump = subparsers.add_parser("bump", help="bump the project version")
group = bump.add_mutually_exclusive_group(required=True)
group.add_argument("--major", action="store_true", help="bump the major version")
group.add_argument("--minor", action="store_true", help="bump the minor version")
group.add_argument("--patch", action="store_true", help="bump the patch version")
group.add_argument(
"version", nargs="?", default=None, help="set an explicit version"
)
subparsers.add_parser("version", help="print the current version")
changelog = subparsers.add_parser("changelog", help="print the changelog")
changelog.add_argument(
"version", nargs="?", default=None, help="version to show (default: latest)"
)
if not len(args) or args[0] not in [*subparsers.choices, "-h", "--help"]:
args = "bump", *args
return parser.parse_args(args)
def main(*argv: str) -> int:
args = parse_args(argv)
if args.command == "bump":
flag_set = args.major or args.minor or args.patch
if flag_set:
version = find_latest_version()
next_version = increment_version(
version, major=args.major, minor=args.minor, patch=args.patch
)
else:
next_version = args.version
try:
bump_version(next_version)
except RuntimeError as e:
sys.exit(f"[!] {e!s}")
elif args.command == "version":
try:
print(find_latest_version(), end="")
except RuntimeError as e:
sys.exit(f"[!] {e!s}")
elif args.command == "changelog":
try:
version = find_latest_version() if args.version is None else args.version
changelog = extract_changelog()
except RuntimeError as e:
sys.exit(f"[!] {e!s}")
if version not in changelog:
sys.exit(f"[!] {version} not in changelog")
print(changelog[version], end="")
return 0
if __name__ == "__main__":
if not sys.argv[1:]:
print("Usage: bump_version.py [new_version]")
else:
main(sys.argv[1])
sys.exit(main(*sys.argv[1:]))
+13
View File
@@ -0,0 +1,13 @@
[files]
"drakrun/web/frontend/package.json" = [
"\"version\": \"$VERSION\"",
]
"drakrun/web/frontend/package-lock.json" = [
"\"name\": \"@drakvuf-sandbox/web\",\\s+\"version\": \"$VERSION\"",
]
"drakrun/version.py" = [
"__version__ = \"$VERSION\"",
]
"docs/conf.py" = [
"release = 'v$VERSION'",
]