[6.7.1] - 2026-07-25
- Updated Starkiller to v3.6.0
Security
- Fixed a path traversal vulnerability where a crafted multipart upload filename could write files outside the downloads directory via the avatar (
POST /api/v2/users/{uid}/avatar) and download (POST /api/v2/downloads/) upload endpoints (#824)
Fixed
- Empire Compiler downloads now authenticate against the GitHub API with
GITHUB_TOKEN/GH_TOKENwhen set, avoiding intermittent403rate-limit failures on shared egress IPs (e.g. CI). When the compiler still can't be fetched, the server degrades gracefully and raises a clear error only if C# compilation is attempted, instead of crashing at startup.
[6.7.0] - 2026-07-06
Fixed
http_malleablelistener no longer crashes withAttributeErrorwhen a request arrives on a URI not defined in the malleable profile. It now returns a 404 default response instead of propagating an unhandled exception.
Added
- Performance indexes migration (0002) — apply with
poetry run alembic -c alembic.ini upgrade head(from the Empire root) on existing databases.
Changed
- Migrated the data-access layer from the SQLAlchemy 1.x Query API to the 2.0
select()idiom across the core services,data_util,jwt_auth, thehttp/http_malleablelisteners, andbasic_reporting. Behavior-preserving and still synchronous;db.query()no longer appears inempire/server. - Migrated the ORM models in
core/db/models.pyfromColumn()/declarative_base()to 2.0 typed declarative (Mapped[]/mapped_column()). Schema-preserving: generated DDL is identical andalembicautogenerate detects no diff. - Modernization quick wins (no behavior change): timezone-aware
datetime.now(UTC)injwt_auth, O(1) startup existence checks incore/db/base.py, removal of the Python-2old_divshim and unusedmath_util.py, and aruffpre-commit bump to matchpyproject.toml. - Bounded the dynamic-PowerShell helper caches with
lru_cacheand made script generation deterministic, cuttingtest_load_modulesfrom ~54s to ~1s on CI. - Trimmed agent-comms SQL hot paths: dropped
lazy="joined"onAgent.host, and useddb.get(Agent, session_id)/ a scalarselect(Agent.hostname)in the hot request paths. - Collapsed the per-result-packet
SessionLocal.begin()loop in_handle_agent_responseso one agent callback opens a single transaction regardless of how many result packets it carries. Per-callback wall time roughly halves for multi-packet batches on MySQL (e.g. 2.0x at N=10, 2.2x at N=25); N=1 is unchanged. Trade-off: a mid-batch failure now rolls back the whole batch instead of partially committing. - Indexed columns the agent-comms / auth hot paths filter on: composite
(listener, archived)onAgent, single-columnAgentFile.session_id,AgentFile.parent_id, andUser.username. Model-only viaindex=True; fresh databases pick the indexes up automatically. profile_service.load_malleable_profilespreloads existingProfilenames once and dedupes in-memory instead of oneSELECTper file (mirrorsmodule_service.load_modules). Duplicate names across categories now log a warning naming the conflicting file instead of being silently caught at commit time.- Lazy-loaded
custom_generatemodule classes. Theimportlib.spec_from_file_location+Module()instantiation now happens on first execute and is cached, so boot skips the file-by-file import work for modules that may never run in a given session. - Replaced three large credential test fixtures with a tiny synthetic module to cut CI obfuscation time; production module sources are unchanged.
- Promoted the
main_menu_mockandmodule_servicefixtures intest_modules.pyto module scope so the five tests share oneModuleService.
Removed
- Dropped 4 unused/redundant Python dependencies to reduce supply-chain surface:
pycryptodome(superseded bycryptographyin the 2021 pycrypto migration; noCrypto.*imports remain),pyOpenSSL(last imports removed in 2020 cleanup, forgotten inpyproject.toml),pytest-timeout(the@pytest.mark.timeoutmarkers that justified it have since been removed), and the redundant directcoveragepin (already pulled transitively bypytest-cov). - Replaced
python-josewithpyjwt.python-joseis effectively abandoned (last release 3.3.0 in 2022) and pulled unmaintainedecdsa/rsatransitive dependencies with a poor security history.pyjwtis the actively-maintained library FastAPI now recommends in its OAuth2/JWT tutorial; the call surface injwt_auth.pyis identical (jwt.encode/jwt.decode), with the exception type changing fromJWTErrortojwt.InvalidTokenError. - Replaced the
SQLAlchemy-Utcdependency with a 90-line vendored module (empire/server/core/db/utc_datetime.py) carrying the upstream MIT notice. The package shipped only two helpers (UtcDateTimecolumn type + dialect-awareutcnow()SQL expression); behavior — including per-dialect SQL — is byte-identical so the existing schema and column defaults are unchanged. - Replaced
requests-filewith inlinefile://dispatch inplugin_service._download_tarusing stdliburllib.parse+Path.open. The dependency existed solely to mount aFileAdapteron arequests.Sessionfor one method; HTTP downloads now userequests.get(...)directly andfile://URLs are handled with a short scheme check. - Modernized the vendored
malleable/profile parser from Py2/Py3 compatibility (six.moves.urllib.*+six.moves.range) to Py3 stdlib (urllib.parse+ builtinrange). Mechanical change —six.moves.urllib.parse↔urllib.parseis a 1:1 rename, andsix.moves.rangeis just the Py3 builtin. Drops thesixdirect dep that was added back whenpython-josewas removed.
Fixed
- Preserved subprocess diagnostics across stager compile failures: the Go/Dotnet compilers now include the return code and fall back to stdout when stderr is empty, and
stager_service.generate_stagersurfaces a 400 instead of a 500 stack trace. - Removed the instance-level
threading.LockinAgentCommunicationServicethat serialized all concurrent agent file writes; also fixed a zero-byte file left on decompression failure and guarded the download-progress calc against a malformedtotal_filesize. - Fixed
clone_git_repoleaking its/tmpstaging directory, which could fill a tmpfs-backed/tmpand cause cascades of unrelated-looking test failures. - Fixed the
update-starkillerrelease action updating the wrongrefinconfig.yaml; it now scopes updates tostarkiller.repo/starkiller.refwithyq. - Fixed
undefer(AgentTask.output_original)referencing a nonexistent attribute (the column isoriginal_output).