Conversation
s3inlc
added this pull request to stack #2501
September 17, 2026 12:36
s3inlc
force-pushed
the
886-stack-1-upload
branch
from
September 18, 2026 12:59
44630aa to
7d0dd1b
Compare
s3inlc
force-pushed
the
886-stack-1-upload
branch
from
September 22, 2026 12:17
7d0dd1b to
910b4b7
Compare
s3inlc
marked this pull request as ready for review
September 22, 2026 12:17
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved critical and moderate issues affect storage permissions, URL handling, validation, authentication, and download correctness.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 5
Open (7)
Include crackers directory in Docker startup ownership handling · New Do not persist download URLs from untrusted Host headers · New Keep legacy URL creation independent of server-side caching · New Limit decoded inline upload size before allocation · New Prevent SSRF through private destinations and redirects · New Reject null downloadUrl updates for external binaries · New Correctly parse suffix and open-ended HTTP ranges · New
What changed in this PR
Adds server-hosted cracker binary uploads and an authenticated download endpoint while preserving agent integration.
Changes:
- Supports inline, imported, and URL-based archive uploads with local storage.
- Adds authenticated streaming downloads with range and ETag support.
- Updates models, migrations, OpenAPI documentation, Docker setup, and tests.
| File | Summary |
|---|---|
src/migrations/postgres/20260917114300_cracker-binary-local-upload.sql |
Adds local filename storage. |
src/migrations/mysql/20260917114300_cracker-binary-local-upload.sql |
Adds filename storage and expands URL capacity. |
src/inc/utils/DownloadUtils.php |
Provides download streaming and range handling. Moderate (3 votes): suffix and open-ended ranges are parsed incorrectly. Moderate (1 vote): 416 responses use an incorrect Content-Range. |
src/inc/utils/CrackerUtils.php |
Handles archive upload, caching, lifecycle, and cleanup. Critical (3 votes): URL-only creation now requires server-side fetching, breaking existing external URLs. Moderate (1 vote): URL edits have the same compatibility break. Critical (3 votes): scheme-only URL validation permits SSRF. Critical (1 vote): inline base64 input is unbounded before size checks. |
src/inc/Util.php |
Adds URL downloading and backend URL generation. Critical (1 vote): request Host can be persisted into agent download URLs. |
src/inc/StartupConfig.php |
Configures cracker storage. |
src/inc/startup/setup.php |
Initializes the cracker directory. |
src/inc/startup/setup.json |
Updates initial cracker data. |
src/inc/downloadapi/DownloadRegistry.php |
Registers download handlers. |
src/inc/downloadapi/DownloadAuthMiddleware.php |
Adds agent and JWT authentication. |
src/inc/downloadapi/DownloadApp.php |
Defines download routes. Moderate (1 vote): IDs accept nonnumeric prefixes. Moderate (1 vote): bearer authentication failures can become HTTP 500 responses. |
src/inc/downloadapi/CrackerBinaryDownloadHandler.php |
Serves local cracker archives. |
src/inc/defines/DDirectories.php |
Adds the cracker directory key. |
src/inc/apiv2/openapi/SpecOverrides.php |
Documents cracker upload fields. |
src/inc/apiv2/openapi/ModelApiPathBuilder.php |
Applies request descriptions. |
src/inc/apiv2/model/CrackerBinaryAPI.php |
Implements upload creation and update rules. Moderate (2 votes): updates allow downloadUrl: null despite the database constraint. |
src/inc/apiv2/common/AbstractHelperAPI.php |
Reuses download utilities. |
src/inc/apiv2/auth/JwtAuthenticationFactory.php |
Shares JWT configuration. |
src/inc/agentapi/model/DownloadBinaryAction.php |
Adds agent-token URLs for local archives. |
src/dba/models/generator.php |
Updates model metadata. |
src/dba/models/CrackerBinaryFactory.php |
Maps the filename field. |
src/dba/models/CrackerBinary.php |
Adds local archive metadata. Moderate (1 vote): nullable API validation conflicts with the non-null database column. |
src/api/v2/index.php |
Uses shared JWT authentication. |
src/api/download.php |
Adds the download API entry point. |
openapi.json |
Updates the generated API specification. Moderate (1 vote): the schema lacks conditional requirements for upload sources. |
Dockerfile |
Creates and configures cracker storage. Critical (2 votes): existing Docker volumes may leave the directory owned by root and unwritable by www-data. |
ci/phpunit/TestBase.php |
Updates test model construction. |
ci/phpunit/inc/UtilTest.php |
Tests backend URL generation. |
ci/phpunit/inc/utils/CrackerUtilsTest.php |
Tests archive lifecycle behavior. |
ci/phpunit/inc/utils/CrackerBinaryUtilsTest.php |
Updates cracker fixtures. |
ci/phpunit/inc/apiv2/openapi/SpecOverridesTest.php |
Tests schema descriptions. |
ci/phpunit/fixtures/openapi/crackerbinarytype.spec.json |
Updates OpenAPI fixtures. |
ci/phpunit/downloadapi/DownloadAppTest.php |
Tests download authentication and streaming. |
ci/phpunit/dba/MassUpdateSetTest.php |
Updates model fixtures. |
ci/phpunit/dba/AbstractModelFactoryTest.php |
Updates model fixtures. |
ci/apiv2/utils.py |
Adds upload and archive test helpers. |
ci/apiv2/test_taskwrapper.py |
Uses valid downloadable archives. |
ci/apiv2/test_cracker.py |
Tests upload, caching, and deletion flows. |
ci/apiv2/test_agent_protocol.py |
Tests agent downloads and token handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| ENV HASHTOPOLIS_LOG_PATH=${HASHTOPOLIS_PATH}/log | ||
| ENV HASHTOPOLIS_CONFIG_PATH=${HASHTOPOLIS_PATH}/config | ||
| ENV HASHTOPOLIS_BINARIES_PATH=${HASHTOPOLIS_PATH}/binaries | ||
| ENV HASHTOPOLIS_CRACKERS_PATH=${HASHTOPOLIS_PATH}/crackers |
Comment on lines
+1329
to
+1330
| } | ||
| return rtrim(Util::buildServerUrl() . SConfig::getInstance()->getVal(DConfig::BASE_URL), '/'); |
Comment on lines
+84
to
+88
| try { | ||
| CrackerUtils::storeLocalCopy($binary); | ||
| } | ||
| catch (HttpError $e) { | ||
| Factory::getCrackerBinaryFactory()->delete($binary); |
Comment on lines
+117
to
+121
| case "inline": | ||
| $archiveData = base64_decode($sourceData, true); | ||
| if ($archiveData === false) { | ||
| throw new HttpError("sourceData not valid base64 encoding"); | ||
| } |
Comment on lines
+133
to
+137
| case "url": | ||
| $scheme = parse_url($sourceData, PHP_URL_SCHEME); | ||
| if ($scheme != "http" && $scheme != "https") { | ||
| throw new HttpError("Only http and https URLs are supported as sourceData!"); | ||
| } |
Comment on lines
+131
to
+133
| $refreshLocalCopy = $binary->getFilename() === null | ||
| && ($data[CrackerBinary::DOWNLOAD_URL] ?? null) !== null | ||
| && $data[CrackerBinary::DOWNLOAD_URL] != $binary->getDownloadUrl(); |
Comment on lines
+43
to
+51
| $range = explode('-', $range); | ||
| $c_start = (int)$range[0]; | ||
| if ((isset($range[1]) && is_numeric($range[1]))) { | ||
| $c_end = (int)$range[1]; | ||
| } | ||
| else { | ||
| $c_end = $size; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Cracker binaries can now be stored on the server itself instead of only referencing an external download URL. Existing URL-based binaries are fully unaffected.
Upload (apiv2)
POST /api/v2/ui/crackersaccepts an alternative todownloadUrl:sourceType/sourceDatawith three sources:import: consume an archive uploaded through the existing chunked (TUS) upload to the import directory — the path for arbitrarily large archives, no PHP request-size limits involvedurl: the server fetches the archive itself (http/https only)inline: base64 archive data for small uploads.7zarchives are accepted (7z magic-byte validation); the archive filename is server-composed ({crackerType}-{version}.7z), and thedownloadUrlis set automatically to the server's download endpoint — no client-supplied filenames or URLsfilenamecolumn onCrackerBinarydoubles as the local-storage flag (NULL = external URL binary, as before)Download
GET /api/download.php/{kind}/{id}(kind registry, extensible to files/hashlists later) — authenticated with either an agent token (?token=) or an apiv2 Bearer JWT; streams with HTTP Range and ETag supportdownloadBinaryaction appends the requesting agent's token to the URL of locally hosted binaries, so the existing fetch-and-extract flow just worksdownloadUrlof local binaries is server-owned and cannot be patched